From e42ee57a50cd031fcd27c8b04c54cdec0f5d1322 Mon Sep 17 00:00:00 2001 From: ekgns33 Date: Tue, 25 Mar 2025 10:17:46 +0900 Subject: [PATCH 01/11] :construction_worker: chore : update ci workflow --- .github/workflows/develop-ci.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/develop-ci.yml b/.github/workflows/develop-ci.yml index e733c501..9e7d9031 100644 --- a/.github/workflows/develop-ci.yml +++ b/.github/workflows/develop-ci.yml @@ -3,8 +3,6 @@ on: push: branches: - main - - 'feat/**' - - 'fix/**' pull_request: types: [ opened, reopened, edited, synchronize ] jobs: @@ -34,6 +32,17 @@ jobs: echo "${{ secrets.ENV_DEV }}" > .env.test shell: bash + - name: touch dev data.sql + run: | + touch src/main/resources/sql/data.sql + echo "${{ secrets.DATA_SQL }}" > src/main/resources/sql/data.sql + shell: bash + - name: touch test data.sql + run: | + touch src/test/resources/sql/data.sql + echo "${{ secrets.TEST_DATA_SQL }}" > src/test/resources/sql/data.sql + shell: bash + - name: Build and analyze run: ./gradlew build From cc7be43c22ae84c442db6e72ab22fa6c0ceab116 Mon Sep 17 00:00:00 2001 From: ekgns33 Date: Tue, 25 Mar 2025 10:19:32 +0900 Subject: [PATCH 02/11] =?UTF-8?q?:wrench:=20chore=20:=20application.yml=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 2 +- src/test/resources/application-test.yml | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 84e0cba4..f2cfdabe 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -102,4 +102,4 @@ spring: on-profile: test sql: init: - mode: never \ No newline at end of file + mode: always \ No newline at end of file diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index 3264ff41..4f302129 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -12,7 +12,7 @@ spring: - optional:file:${ENV_PATH:.}/.env.${SPRING_PROFILES_ACTIVE:test}[.properties] datasource: driver-class-name: org.h2.Driver - url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL username: ${DB_USERNAME} password: ${DB_PASSWORD} jpa: @@ -22,8 +22,17 @@ spring: hibernate: dialect: org.hibernate.dialect.H2Dialect open-in-view: false + show-sql: true + defer-datasource-initialization: true jackson: property-naming-strategy: SNAKE_CASE + + sql: + init: + mode: always + schema-locations: classpath*:sql/schema.sql + data-locations: classpath*:sql/data.sql + security: oauth2: client: From 860a96e3cde756d3ae6104261688e70b45ae9869 Mon Sep 17 00:00:00 2001 From: ekgns33 Date: Tue, 25 Mar 2025 10:20:15 +0900 Subject: [PATCH 03/11] =?UTF-8?q?:hammer:=20chore=20:=20sql=20ddl=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/sql/schema.sql | 127 ++++++++++++++++++++++++++++++ src/test/resources/sql/schema.sql | 110 ++++++++++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 src/main/resources/sql/schema.sql create mode 100644 src/test/resources/sql/schema.sql diff --git a/src/main/resources/sql/schema.sql b/src/main/resources/sql/schema.sql new file mode 100644 index 00000000..d13df5d6 --- /dev/null +++ b/src/main/resources/sql/schema.sql @@ -0,0 +1,127 @@ +SET FOREIGN_KEY_CHECKS = 0; + +DROP TABLE IF EXISTS user_token; +DROP TABLE IF EXISTS oauth_accounts; +DROP TABLE IF EXISTS running_records; +DROP TABLE IF EXISTS user_item; +DROP TABLE IF EXISTS incubator; +DROP TABLE IF EXISTS user_runimo; +DROP TABLE IF EXISTS item_activity; +DROP TABLE IF EXISTS runimo; +DROP TABLE IF EXISTS items; +DROP TABLE IF EXISTS users; + +SET FOREIGN_KEY_CHECKS = 1; + +CREATE TABLE `users` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `public_id` VARCHAR(255), + `nickname` VARCHAR(255), + `img_url` VARCHAR(255), + `total_distance_in_meters` BIGINT NOT NULL DEFAULT 0, + `total_time_in_seconds` BIGINT NOT NULL DEFAULT 0, + `updated_at` TIMESTAMP, + `created_at` TIMESTAMP, + `deleted_at` TIMESTAMP +); + +CREATE TABLE `user_token` ( + `user_id` BIGINT NOT NULL, + `device_token` VARCHAR(255) NOT NULL, + `created_at` TIMESTAMP, + `updated_at` TIMESTAMP, + `deleted_at` TIMESTAMP, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE +); + +CREATE TABLE `oauth_accounts` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `provider` VARCHAR(255), + `provider_id` VARCHAR(255), + `created_at` TIMESTAMP, + `updated_at` TIMESTAMP, + `deleted_at` TIMESTAMP, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE +); + +CREATE TABLE `running_records` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `record_public_id` VARCHAR(255) NOT NULL, + `start_at` TIMESTAMP, + `end_at` TIMESTAMP, + `total_distance` BIGINT, + `pace_in_milli_seconds` BIGINT, + `created_at` TIMESTAMP, + `updated_at` TIMESTAMP, + `deleted_at` TIMESTAMP +); + +CREATE TABLE `items` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `item_code` VARCHAR(255) NOT NULL, + `description` VARCHAR(255), + `item_type` VARCHAR(255) NOT NULL, + `img_url` VARCHAR(255), + `dtype` VARCHAR(255), + `egg_type` VARCHAR(255), + `hatch_require_amount` BIGINT, + `created_at` TIMESTAMP, + `updated_at` TIMESTAMP, + `deleted_at` TIMESTAMP +); + +CREATE TABLE `item_activity` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `activity_user_id` BIGINT NOT NULL, + `activity_item_id` BIGINT NOT NULL, + `activity_event_type` VARCHAR(255) NOT NULL, + `created_at` TIMESTAMP, + `updated_at` TIMESTAMP, + `deleted_at` TIMESTAMP +); + +CREATE TABLE `user_item` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `item_id` BIGINT NOT NULL, + `quantity` BIGINT NOT NULL, + `created_at` TIMESTAMP, + `updated_at` TIMESTAMP, + `deleted_at` TIMESTAMP +); + +CREATE TABLE `incubator` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `slot` BIGINT NOT NULL, + `egg_id` BIGINT NOT NULL, + `progress` BIGINT DEFAULT 0, + `created_at` TIMESTAMP, + `updated_at` TIMESTAMP, + `deleted_at` TIMESTAMP +); + +CREATE TABLE `runimo` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `name` VARCHAR(255), + `description` VARCHAR(255), + `type` VARCHAR(255) NOT NULL, + `created_at` TIMESTAMP, + `updated_at` TIMESTAMP, + `deleted_at` TIMESTAMP +); + +CREATE TABLE `user_runimo` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `runimo_id` BIGINT NOT NULL, + `created_at` TIMESTAMP, + `updated_at` TIMESTAMP, + `deleted_at` TIMESTAMP +); + +ALTER TABLE `user_token` ADD FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE; +ALTER TABLE `oauth_accounts` ADD FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE; \ No newline at end of file diff --git a/src/test/resources/sql/schema.sql b/src/test/resources/sql/schema.sql new file mode 100644 index 00000000..fa4e7a14 --- /dev/null +++ b/src/test/resources/sql/schema.sql @@ -0,0 +1,110 @@ +SET FOREIGN_KEY_CHECKS = 0; + +DROP TABLE IF EXISTS user_token; +DROP TABLE IF EXISTS oauth_accounts; +DROP TABLE IF EXISTS running_records; +DROP TABLE IF EXISTS user_item; +DROP TABLE IF EXISTS incubator; +DROP TABLE IF EXISTS user_runimo; +DROP TABLE IF EXISTS item_activity; +DROP TABLE IF EXISTS runimo; +DROP TABLE IF EXISTS items; +DROP TABLE IF EXISTS users; + +SET FOREIGN_KEY_CHECKS = 1; + +CREATE TABLE `users` +( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `public_id` VARCHAR(255), + `nickname` VARCHAR(255), + `img_url` VARCHAR(255), + `total_distance_in_meters` BIGINT NOT NULL DEFAULT 0, + `total_time_in_seconds` BIGINT NOT NULL DEFAULT 0, + `updated_at` TIMESTAMP, + `created_at` TIMESTAMP, + `deleted_at` TIMESTAMP +); + +CREATE TABLE `user_token` +( + `user_id` BIGINT NOT NULL, + `device_token` VARCHAR(255) NOT NULL, + `created_at` TIMESTAMP, + `updated_at` TIMESTAMP, + `deleted_at` TIMESTAMP, + + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +); + +CREATE TABLE `oauth_accounts` +( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `provider` VARCHAR(255), + `provider_id` VARCHAR(255), + `created_at` TIMESTAMP, + `updated_at` TIMESTAMP, + `deleted_at` TIMESTAMP, + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +); + + +CREATE TABLE `running_records` +( + `id` integer PRIMARY KEY AUTO_INCREMENT, + `user_id` integer NOT NULL, + `record_public_id` VARCHAR(255) NOT NULL, + `title` varchar(255), + `started_at` timestamp, + `end_at` timestamp, + `total_distance` integer, + `pace_in_milli_seconds` integer, + `created_at` timestamp, + `updated_at` timestamp, + `deleted_at` TIMESTAMP +); + +CREATE TABLE `items` +( + `id` integer PRIMARY KEY AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `item_code` varchar(255) NOT NULL, + `description` varchar(255), + `item_type` varchar(255) NOT NULL, + `img_url` varchar(255), + `dtype` varchar(255), + `egg_type` varchar(255), + `hatch_require_amount` long, + `created_at` timestamp, + `updated_at` timestamp, + `deleted_at` TIMESTAMP +); + +CREATE TABLE `item_activity` +( + `id` integer PRIMARY KEY AUTO_INCREMENT, + `activity_user_id` integer NOT NULL, + `activity_item_id` integer NOT NULL, + `activity_event_type` varchar(255) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + `deleted_at` TIMESTAMP +); + +CREATE TABLE `user_item` +( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `user_id` integer NOT NULL, + `item_id` integer NOT NULL, + `quantity` integer NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + `deleted_at` TIMESTAMP +); + +ALTER TABLE `user_token` + ADD FOREIGN KEY (`user_id`) REFERENCES `users` (`id`); + +ALTER TABLE `oauth_accounts` + ADD FOREIGN KEY (`user_id`) REFERENCES `users` (`id`); From 0b67d47b1cfc6c31fedfdaab0df03fc6ad8cb310 Mon Sep 17 00:00:00 2001 From: ekgns33 Date: Tue, 25 Mar 2025 10:22:48 +0900 Subject: [PATCH 04/11] =?UTF-8?q?:sparkles:=20feat=20:=20jpa=20entity?= =?UTF-8?q?=EC=97=90=20=EC=BB=AC=EB=9F=BC=EB=AA=85,=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EB=AA=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/runimo/runimo/common/scale/Pace.java | 2 ++ .../org/runimo/runimo/item/domain/Item.java | 21 +++++++++++------- .../runimo/item/domain/ItemActivity.java | 2 ++ .../runimo/records/domain/RunningRecord.java | 5 +++-- .../org/runimo/runimo/user/domain/User.java | 22 +++++++++++++++++-- .../runimo/runimo/user/domain/UserItem.java | 4 +++- 6 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/runimo/runimo/common/scale/Pace.java b/src/main/java/org/runimo/runimo/common/scale/Pace.java index 4f8d6732..5c385861 100644 --- a/src/main/java/org/runimo/runimo/common/scale/Pace.java +++ b/src/main/java/org/runimo/runimo/common/scale/Pace.java @@ -1,5 +1,6 @@ package org.runimo.runimo.common.scale; +import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -12,6 +13,7 @@ public class Pace implements Serializable { @Serial private static final long serialVersionUID = 1L; + @Column(name = "pace_in_milli_seconds") private Long paceInMilliSeconds; public Pace(Long paceInMilliSeconds) { diff --git a/src/main/java/org/runimo/runimo/item/domain/Item.java b/src/main/java/org/runimo/runimo/item/domain/Item.java index df0443b9..be77a43d 100644 --- a/src/main/java/org/runimo/runimo/item/domain/Item.java +++ b/src/main/java/org/runimo/runimo/item/domain/Item.java @@ -1,32 +1,37 @@ package org.runimo.runimo.item.domain; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; +import jakarta.persistence.*; import lombok.AccessLevel; -import lombok.Builder; +import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.NaturalId; import org.runimo.runimo.common.BaseEntity; +@Table(name = "items") @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Item extends BaseEntity { +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name = "dtype") +@Getter +public abstract class Item extends BaseEntity { + @Column(name = "item_code", nullable = false, unique = true) @NaturalId private String itemCode; + @Column(name = "name", nullable = false) private String name; - + @Column(name = "description") private String description; + @Column(name = "img_url") private String imgUrl; + @Column(name = "item_type", nullable = false) @Enumerated(EnumType.STRING) private ItemType itemType; - @Builder - public Item(String itemCode, String name, String description, String imgUrl, ItemType itemType) { + protected Item(String itemCode, String name, String description, String imgUrl, ItemType itemType) { this.itemCode = itemCode; this.name = name; this.description = description; diff --git a/src/main/java/org/runimo/runimo/item/domain/ItemActivity.java b/src/main/java/org/runimo/runimo/item/domain/ItemActivity.java index a618e2a4..9fff3689 100644 --- a/src/main/java/org/runimo/runimo/item/domain/ItemActivity.java +++ b/src/main/java/org/runimo/runimo/item/domain/ItemActivity.java @@ -2,6 +2,7 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -9,6 +10,7 @@ import org.runimo.runimo.common.BaseEntity; // append only entity +@Table(name = "item_activity") @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) diff --git a/src/main/java/org/runimo/runimo/records/domain/RunningRecord.java b/src/main/java/org/runimo/runimo/records/domain/RunningRecord.java index a8a4af01..e0c13e5a 100644 --- a/src/main/java/org/runimo/runimo/records/domain/RunningRecord.java +++ b/src/main/java/org/runimo/runimo/records/domain/RunningRecord.java @@ -1,8 +1,7 @@ package org.runimo.runimo.records.domain; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; +import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -15,6 +14,7 @@ import java.util.Objects; import java.util.UUID; +@Table(name = "running_records") @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @@ -25,6 +25,7 @@ public class RunningRecord extends BaseEntity { private LocalDateTime startedAt; private LocalDateTime endAt; @Embedded + @AttributeOverride(name = "amount", column = @Column(name = "total_distance")) private Distance totalDistance; @Embedded private Pace averagePace; diff --git a/src/main/java/org/runimo/runimo/user/domain/User.java b/src/main/java/org/runimo/runimo/user/domain/User.java index 44199104..4efa620b 100644 --- a/src/main/java/org/runimo/runimo/user/domain/User.java +++ b/src/main/java/org/runimo/runimo/user/domain/User.java @@ -1,5 +1,6 @@ package org.runimo.runimo.user.domain; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.PrePersist; import jakarta.persistence.Table; @@ -16,15 +17,32 @@ @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class User extends BaseEntity { - + @Column(name = "public_id", nullable = false) private String publicId; + @Column(name = "nickname") private String nickname; + @Column(name = "img_url") private String imgUrl; + @Column(name = "total_distance_in_meters", nullable = false) + private Long totalDistanceInMeters = 0L; + @Column(name = "total_time_in_seconds", nullable = false) + private Long totalTimeInSeconds = 0L; @Builder - public User(String nickname, String imgUrl) { + public User(String nickname, String imgUrl, Long totalDistanceInMeters, Long totalTimeInSeconds) { this.nickname = nickname; this.imgUrl = imgUrl; + this.totalDistanceInMeters = totalDistanceInMeters != null ? totalDistanceInMeters : 0L; + this.totalTimeInSeconds = totalTimeInSeconds != null ? totalTimeInSeconds : 0L; + } + + public boolean checkUserFirstRun() { + return totalDistanceInMeters == 0L && totalTimeInSeconds == 0L; + } + + public void updateRunningStats(Long distance, Long time) { + this.totalDistanceInMeters += distance; + this.totalTimeInSeconds += time; } @PrePersist diff --git a/src/main/java/org/runimo/runimo/user/domain/UserItem.java b/src/main/java/org/runimo/runimo/user/domain/UserItem.java index 9d77275f..4d7f01e1 100644 --- a/src/main/java/org/runimo/runimo/user/domain/UserItem.java +++ b/src/main/java/org/runimo/runimo/user/domain/UserItem.java @@ -1,12 +1,14 @@ package org.runimo.runimo.user.domain; import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import org.runimo.runimo.common.BaseEntity; +@Table(name = "user_item") @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -37,6 +39,6 @@ private void updateQuantity(Long quantity) { } private void validateQuantity(Long quantity) { - if(quantity < 0) throw new IllegalArgumentException("quantity must be greater than zero"); + if (quantity < 0) throw new IllegalArgumentException("quantity must be greater than zero"); } } From 30c57080bfa629f0db7ba27c0d7dbdf70f9a22ce Mon Sep 17 00:00:00 2001 From: ekgns33 Date: Tue, 25 Mar 2025 10:26:03 +0900 Subject: [PATCH 05/11] =?UTF-8?q?:sparkles:=20feat=20:=20=EC=95=8C=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=EC=9E=91=EC=84=B1,=20=EC=A7=80?= =?UTF-8?q?=EA=B8=89=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/runimo/runimo/item/domain/Egg.java | 28 +++++++++++++++++ .../runimo/runimo/item/domain/EggType.java | 30 ++++++++++++++++++ .../item/repository/ItemRepository.java | 6 ++++ .../runimo/item/service/EggFactory.java | 31 +++++++++++++++++++ .../item/service/EggTypeRandomGenerator.java | 26 ++++++++++++++++ .../runimo/item/service/ItemFinder.java | 8 +++++ 6 files changed, 129 insertions(+) create mode 100644 src/main/java/org/runimo/runimo/item/domain/Egg.java create mode 100644 src/main/java/org/runimo/runimo/item/domain/EggType.java create mode 100644 src/main/java/org/runimo/runimo/item/service/EggFactory.java create mode 100644 src/main/java/org/runimo/runimo/item/service/EggTypeRandomGenerator.java diff --git a/src/main/java/org/runimo/runimo/item/domain/Egg.java b/src/main/java/org/runimo/runimo/item/domain/Egg.java new file mode 100644 index 00000000..372def4f --- /dev/null +++ b/src/main/java/org/runimo/runimo/item/domain/Egg.java @@ -0,0 +1,28 @@ +package org.runimo.runimo.item.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Egg extends Item { + @Column(name = "egg_type") + @Enumerated(EnumType.STRING) + private EggType eggType; + @Column(name = "hatch_require_amount") + private Long hatchRequireAmount; + + @Builder + public Egg(String itemCode, String name, String description, String imgUrl, EggType eggType, Long hatchRequireAmount) { + super(itemCode, name, description, imgUrl, ItemType.USABLE); + this.eggType = eggType; + this.hatchRequireAmount = hatchRequireAmount; + } +} \ No newline at end of file diff --git a/src/main/java/org/runimo/runimo/item/domain/EggType.java b/src/main/java/org/runimo/runimo/item/domain/EggType.java new file mode 100644 index 00000000..3042b4b7 --- /dev/null +++ b/src/main/java/org/runimo/runimo/item/domain/EggType.java @@ -0,0 +1,30 @@ +package org.runimo.runimo.item.domain; + +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; + +@Getter +public enum EggType { + MADANG("A100", "마당", 0L), + FOREST("A101", "숲", 30000L), + GRASSLAND("A102", "초원", 50000L); + + private final String code; + private final String name; + private final Long requiredDistanceInMeters; + + + EggType(String code, String name, Long requiredDistanceInMeters) { + this.code = code; + this.name = name; + this.requiredDistanceInMeters = requiredDistanceInMeters; + } + + public static List getUnLockedEggTypes(final Long distance) { + return Arrays.stream(EggType.values()) + .filter(type -> type.requiredDistanceInMeters < distance) + .toList(); + } +} diff --git a/src/main/java/org/runimo/runimo/item/repository/ItemRepository.java b/src/main/java/org/runimo/runimo/item/repository/ItemRepository.java index f60e5497..279e7de6 100644 --- a/src/main/java/org/runimo/runimo/item/repository/ItemRepository.java +++ b/src/main/java/org/runimo/runimo/item/repository/ItemRepository.java @@ -1,15 +1,21 @@ package org.runimo.runimo.item.repository; +import org.runimo.runimo.item.domain.Egg; +import org.runimo.runimo.item.domain.EggType; import org.runimo.runimo.item.domain.Item; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository public interface ItemRepository extends JpaRepository { @Query("select i.id from Item i") List findAllItemIds(); + + @Query("select e from Egg e where e.eggType = :eggtype") + Optional findByEggType(EggType eggtype); } diff --git a/src/main/java/org/runimo/runimo/item/service/EggFactory.java b/src/main/java/org/runimo/runimo/item/service/EggFactory.java new file mode 100644 index 00000000..673871fe --- /dev/null +++ b/src/main/java/org/runimo/runimo/item/service/EggFactory.java @@ -0,0 +1,31 @@ +package org.runimo.runimo.item.service; + +import lombok.RequiredArgsConstructor; +import org.runimo.runimo.item.domain.Egg; +import org.runimo.runimo.item.domain.EggType; +import org.springframework.stereotype.Component; + +import java.util.List; + +/* + * 리워드로 지급되는 알의 생성을 담당하는 클래스 + * */ +@Component +@RequiredArgsConstructor +public class EggFactory { + + private final ItemFinder itemFinder; + private final EggTypeRandomGenerator eggTypeRandomGenerator; + + public Egg createGreetingEgg() { + return itemFinder.findEggByEggType(EggType.MADANG) + .orElseThrow(RuntimeException::new); + } + + // 0 ~ 1 사이의 난수를 생성하여 그에 따라 알을 생성 + public Egg createEggRandomly(List unLockedEggTypes) { + EggType selectedType = eggTypeRandomGenerator.generateRandomEggType(unLockedEggTypes); + return itemFinder.findEggByEggType(selectedType) + .orElseThrow(RuntimeException::new); + } +} diff --git a/src/main/java/org/runimo/runimo/item/service/EggTypeRandomGenerator.java b/src/main/java/org/runimo/runimo/item/service/EggTypeRandomGenerator.java new file mode 100644 index 00000000..f475de08 --- /dev/null +++ b/src/main/java/org/runimo/runimo/item/service/EggTypeRandomGenerator.java @@ -0,0 +1,26 @@ +package org.runimo.runimo.item.service; + +import org.runimo.runimo.item.domain.EggType; +import org.springframework.stereotype.Component; + +import java.security.SecureRandom; +import java.util.List; + +@Component +public class EggTypeRandomGenerator { + + private final SecureRandom secureRandom = new SecureRandom(); + + public EggType generateRandomEggType(List unLockedEggTypes) { + double eachEggTypeProbability = 1.0 / unLockedEggTypes.size(); + double cumulatedProbability = 0.0; + double randomValue = secureRandom.nextDouble(); + for (EggType eggType : unLockedEggTypes) { + cumulatedProbability += eachEggTypeProbability; + if (randomValue < cumulatedProbability) { + return eggType; + } + } + return unLockedEggTypes.getLast(); + } +} diff --git a/src/main/java/org/runimo/runimo/item/service/ItemFinder.java b/src/main/java/org/runimo/runimo/item/service/ItemFinder.java index 74a95389..21c1e161 100644 --- a/src/main/java/org/runimo/runimo/item/service/ItemFinder.java +++ b/src/main/java/org/runimo/runimo/item/service/ItemFinder.java @@ -1,14 +1,18 @@ package org.runimo.runimo.item.service; import lombok.RequiredArgsConstructor; +import org.runimo.runimo.item.domain.Egg; +import org.runimo.runimo.item.domain.EggType; import org.runimo.runimo.item.domain.Item; import org.runimo.runimo.item.repository.ItemRepository; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import java.util.Optional; @Component @RequiredArgsConstructor +@Transactional(readOnly = true) public class ItemFinder { private final ItemRepository itemRepository; @@ -17,6 +21,10 @@ public Optional findById(Long itemId) { return itemRepository.findById(itemId); } + public Optional findEggByEggType(EggType eggtype) { + return itemRepository.findByEggType(eggtype); + } + public Boolean isItemExist(Long itemId) { return itemRepository.existsById(itemId); } From db7afb36864409bae266fff786dfbb040ddda886 Mon Sep 17 00:00:00 2001 From: ekgns33 Date: Tue, 25 Mar 2025 10:28:24 +0900 Subject: [PATCH 06/11] =?UTF-8?q?:recycle:=20refactor=20:=20=EB=94=94?= =?UTF-8?q?=EB=A0=89=ED=86=A0=EB=A6=AC=EB=AA=85=20=ED=86=B5=EC=9D=BC,=20im?= =?UTF-8?q?port=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runimo/auth/service/OidcNonceService.java | 2 +- .../item/service/ItemActivityCreatorImpl.java | 2 +- .../item/service/dtos/CreateActivityCommand.java | 8 ++++---- .../runimo/records/controller/RecordController.java | 10 +++++----- .../{model => requests}/RecordSaveRequest.java | 2 +- .../{model => requests}/RecordUpdateRequest.java | 4 ++-- .../records/service/RecordCommandService.java | 6 +++--- .../service/usecases/RecordCreateUsecase.java | 4 ++-- .../service/usecases/RecordCreateUsecaseImpl.java | 9 +++++++-- .../service/usecases/RecordQueryUsecase.java | 2 +- .../service/usecases/RecordQueryUsecaseImpl.java | 2 +- .../service/usecases/RecordUpdateUsecase.java | 2 +- .../service/usecases/RecordUpdateUsecaseImpl.java | 2 +- .../{model => dtos}/RecordCreateCommand.java | 13 +++++++++++-- .../{model => dtos}/RecordDetailViewResponse.java | 2 +- .../service/usecases/dtos/RecordSaveResponse.java | 5 +++++ .../{model => dtos}/RecordUpdateCommand.java | 2 +- .../service/usecases/model/RecordSaveResponse.java | 5 ----- 18 files changed, 48 insertions(+), 34 deletions(-) rename src/main/java/org/runimo/runimo/records/controller/{model => requests}/RecordSaveRequest.java (93%) rename src/main/java/org/runimo/runimo/records/controller/{model => requests}/RecordUpdateRequest.java (89%) rename src/main/java/org/runimo/runimo/records/service/usecases/{model => dtos}/RecordCreateCommand.java (63%) rename src/main/java/org/runimo/runimo/records/service/usecases/{model => dtos}/RecordDetailViewResponse.java (93%) create mode 100644 src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordSaveResponse.java rename src/main/java/org/runimo/runimo/records/service/usecases/{model => dtos}/RecordUpdateCommand.java (83%) delete mode 100644 src/main/java/org/runimo/runimo/records/service/usecases/model/RecordSaveResponse.java diff --git a/src/main/java/org/runimo/runimo/auth/service/OidcNonceService.java b/src/main/java/org/runimo/runimo/auth/service/OidcNonceService.java index b790874e..576c26fd 100644 --- a/src/main/java/org/runimo/runimo/auth/service/OidcNonceService.java +++ b/src/main/java/org/runimo/runimo/auth/service/OidcNonceService.java @@ -15,7 +15,7 @@ @RequiredArgsConstructor public class OidcNonceService { - private final static String NONCE_CLAIM_KEY = "nonce"; + private static final String NONCE_CLAIM_KEY = "nonce"; private final OAuthTokenRepository oAuthTokenRepository; public void checkNonceAndSave(final SocialProvider provider, final DecodedJWT decodedJWT) { diff --git a/src/main/java/org/runimo/runimo/item/service/ItemActivityCreatorImpl.java b/src/main/java/org/runimo/runimo/item/service/ItemActivityCreatorImpl.java index e8d68079..812e8208 100644 --- a/src/main/java/org/runimo/runimo/item/service/ItemActivityCreatorImpl.java +++ b/src/main/java/org/runimo/runimo/item/service/ItemActivityCreatorImpl.java @@ -1,8 +1,8 @@ package org.runimo.runimo.item.service; import lombok.RequiredArgsConstructor; -import org.runimo.runimo.item.repository.ItemActivityRepository; import org.runimo.runimo.item.domain.ItemActivity; +import org.runimo.runimo.item.repository.ItemActivityRepository; import org.runimo.runimo.item.service.dtos.CreateActivityCommand; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/org/runimo/runimo/item/service/dtos/CreateActivityCommand.java b/src/main/java/org/runimo/runimo/item/service/dtos/CreateActivityCommand.java index 1ddb4ee7..cb3e1ee1 100644 --- a/src/main/java/org/runimo/runimo/item/service/dtos/CreateActivityCommand.java +++ b/src/main/java/org/runimo/runimo/item/service/dtos/CreateActivityCommand.java @@ -3,9 +3,9 @@ import org.runimo.runimo.item.domain.ActivityType; public record CreateActivityCommand( - Long itemId, - Long userId, - Long quantity, - ActivityType activityType + Long itemId, + Long userId, + Long quantity, + ActivityType activityType ) { } diff --git a/src/main/java/org/runimo/runimo/records/controller/RecordController.java b/src/main/java/org/runimo/runimo/records/controller/RecordController.java index d15dff12..20b5fbdb 100644 --- a/src/main/java/org/runimo/runimo/records/controller/RecordController.java +++ b/src/main/java/org/runimo/runimo/records/controller/RecordController.java @@ -7,14 +7,14 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import org.runimo.runimo.records.controller.model.RecordSaveRequest; -import org.runimo.runimo.records.controller.model.RecordUpdateRequest; +import org.runimo.runimo.records.controller.requests.RecordSaveRequest; +import org.runimo.runimo.records.controller.requests.RecordUpdateRequest; import org.runimo.runimo.records.service.usecases.RecordCreateUsecase; import org.runimo.runimo.records.service.usecases.RecordQueryUsecase; import org.runimo.runimo.records.service.usecases.RecordUpdateUsecase; -import org.runimo.runimo.records.service.usecases.model.RecordCreateCommand; -import org.runimo.runimo.records.service.usecases.model.RecordDetailViewResponse; -import org.runimo.runimo.records.service.usecases.model.RecordSaveResponse; +import org.runimo.runimo.records.service.usecases.dtos.RecordCreateCommand; +import org.runimo.runimo.records.service.usecases.dtos.RecordDetailViewResponse; +import org.runimo.runimo.records.service.usecases.dtos.RecordSaveResponse; import org.runimo.runimo.user.controller.UserId; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/org/runimo/runimo/records/controller/model/RecordSaveRequest.java b/src/main/java/org/runimo/runimo/records/controller/requests/RecordSaveRequest.java similarity index 93% rename from src/main/java/org/runimo/runimo/records/controller/model/RecordSaveRequest.java rename to src/main/java/org/runimo/runimo/records/controller/requests/RecordSaveRequest.java index 0f92074a..f9ea60bd 100644 --- a/src/main/java/org/runimo/runimo/records/controller/model/RecordSaveRequest.java +++ b/src/main/java/org/runimo/runimo/records/controller/requests/RecordSaveRequest.java @@ -1,4 +1,4 @@ -package org.runimo.runimo.records.controller.model; +package org.runimo.runimo.records.controller.requests; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/src/main/java/org/runimo/runimo/records/controller/model/RecordUpdateRequest.java b/src/main/java/org/runimo/runimo/records/controller/requests/RecordUpdateRequest.java similarity index 89% rename from src/main/java/org/runimo/runimo/records/controller/model/RecordUpdateRequest.java rename to src/main/java/org/runimo/runimo/records/controller/requests/RecordUpdateRequest.java index 2b17845a..d19e05c2 100644 --- a/src/main/java/org/runimo/runimo/records/controller/model/RecordUpdateRequest.java +++ b/src/main/java/org/runimo/runimo/records/controller/requests/RecordUpdateRequest.java @@ -1,8 +1,8 @@ -package org.runimo.runimo.records.controller.model; +package org.runimo.runimo.records.controller.requests; import io.swagger.v3.oas.annotations.media.Schema; -import org.runimo.runimo.records.service.usecases.model.RecordUpdateCommand; +import org.runimo.runimo.records.service.usecases.dtos.RecordUpdateCommand; import java.time.LocalDateTime; diff --git a/src/main/java/org/runimo/runimo/records/service/RecordCommandService.java b/src/main/java/org/runimo/runimo/records/service/RecordCommandService.java index 85a53e74..6f3a81bb 100644 --- a/src/main/java/org/runimo/runimo/records/service/RecordCommandService.java +++ b/src/main/java/org/runimo/runimo/records/service/RecordCommandService.java @@ -5,9 +5,9 @@ import org.runimo.runimo.common.scale.Pace; import org.runimo.runimo.records.domain.RunningRecord; import org.runimo.runimo.records.repository.RecordRepository; -import org.runimo.runimo.records.service.usecases.model.RecordCreateCommand; -import org.runimo.runimo.records.service.usecases.model.RecordSaveResponse; -import org.runimo.runimo.records.service.usecases.model.RecordUpdateCommand; +import org.runimo.runimo.records.service.usecases.dtos.RecordCreateCommand; +import org.runimo.runimo.records.service.usecases.dtos.RecordSaveResponse; +import org.runimo.runimo.records.service.usecases.dtos.RecordUpdateCommand; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/RecordCreateUsecase.java b/src/main/java/org/runimo/runimo/records/service/usecases/RecordCreateUsecase.java index 84bffada..d43d873f 100644 --- a/src/main/java/org/runimo/runimo/records/service/usecases/RecordCreateUsecase.java +++ b/src/main/java/org/runimo/runimo/records/service/usecases/RecordCreateUsecase.java @@ -1,7 +1,7 @@ package org.runimo.runimo.records.service.usecases; -import org.runimo.runimo.records.service.usecases.model.RecordCreateCommand; -import org.runimo.runimo.records.service.usecases.model.RecordSaveResponse; +import org.runimo.runimo.records.service.usecases.dtos.RecordCreateCommand; +import org.runimo.runimo.records.service.usecases.dtos.RecordSaveResponse; public interface RecordCreateUsecase { RecordSaveResponse execute(RecordCreateCommand command); diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/RecordCreateUsecaseImpl.java b/src/main/java/org/runimo/runimo/records/service/usecases/RecordCreateUsecaseImpl.java index 2ae9048c..7b16f01b 100644 --- a/src/main/java/org/runimo/runimo/records/service/usecases/RecordCreateUsecaseImpl.java +++ b/src/main/java/org/runimo/runimo/records/service/usecases/RecordCreateUsecaseImpl.java @@ -2,11 +2,13 @@ import lombok.RequiredArgsConstructor; import org.runimo.runimo.records.service.RecordCommandService; -import org.runimo.runimo.records.service.usecases.model.RecordCreateCommand; -import org.runimo.runimo.records.service.usecases.model.RecordSaveResponse; +import org.runimo.runimo.records.service.usecases.dtos.RecordCreateCommand; +import org.runimo.runimo.records.service.usecases.dtos.RecordSaveResponse; import org.runimo.runimo.user.domain.User; import org.runimo.runimo.user.service.UserFinder; +import org.runimo.runimo.user.service.UserStatService; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.NoSuchElementException; @@ -15,12 +17,15 @@ public class RecordCreateUsecaseImpl implements RecordCreateUsecase { private final UserFinder userFinder; + private final UserStatService userStatService; private final RecordCommandService commandService; @Override + @Transactional public RecordSaveResponse execute(RecordCreateCommand command) { User user = userFinder.findUserByPublicId(command.userPublicId()) .orElseThrow(NoSuchElementException::new); + userStatService.updateUserStats(user, command); return commandService.saveRecord(user.getId(), command); } } diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecase.java b/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecase.java index c8201689..39179131 100644 --- a/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecase.java +++ b/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecase.java @@ -1,6 +1,6 @@ package org.runimo.runimo.records.service.usecases; -import org.runimo.runimo.records.service.usecases.model.RecordDetailViewResponse; +import org.runimo.runimo.records.service.usecases.dtos.RecordDetailViewResponse; public interface RecordQueryUsecase { RecordDetailViewResponse getRecordDetailView(String publicId); diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecaseImpl.java b/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecaseImpl.java index a284ab95..9bb8d7f1 100644 --- a/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecaseImpl.java +++ b/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecaseImpl.java @@ -3,7 +3,7 @@ import lombok.RequiredArgsConstructor; import org.runimo.runimo.records.domain.RunningRecord; import org.runimo.runimo.records.service.RecordFinder; -import org.runimo.runimo.records.service.usecases.model.RecordDetailViewResponse; +import org.runimo.runimo.records.service.usecases.dtos.RecordDetailViewResponse; import org.springframework.stereotype.Service; import java.util.NoSuchElementException; diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/RecordUpdateUsecase.java b/src/main/java/org/runimo/runimo/records/service/usecases/RecordUpdateUsecase.java index ef39d77f..46b5cee6 100644 --- a/src/main/java/org/runimo/runimo/records/service/usecases/RecordUpdateUsecase.java +++ b/src/main/java/org/runimo/runimo/records/service/usecases/RecordUpdateUsecase.java @@ -1,6 +1,6 @@ package org.runimo.runimo.records.service.usecases; -import org.runimo.runimo.records.service.usecases.model.RecordUpdateCommand; +import org.runimo.runimo.records.service.usecases.dtos.RecordUpdateCommand; public interface RecordUpdateUsecase { void updateRecord(RecordUpdateCommand command); diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/RecordUpdateUsecaseImpl.java b/src/main/java/org/runimo/runimo/records/service/usecases/RecordUpdateUsecaseImpl.java index dfe7476a..b50af3c6 100644 --- a/src/main/java/org/runimo/runimo/records/service/usecases/RecordUpdateUsecaseImpl.java +++ b/src/main/java/org/runimo/runimo/records/service/usecases/RecordUpdateUsecaseImpl.java @@ -2,7 +2,7 @@ import lombok.RequiredArgsConstructor; import org.runimo.runimo.records.service.RecordCommandService; -import org.runimo.runimo.records.service.usecases.model.RecordUpdateCommand; +import org.runimo.runimo.records.service.usecases.dtos.RecordUpdateCommand; import org.runimo.runimo.user.service.UserFinder; import org.springframework.stereotype.Service; diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/model/RecordCreateCommand.java b/src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordCreateCommand.java similarity index 63% rename from src/main/java/org/runimo/runimo/records/service/usecases/model/RecordCreateCommand.java rename to src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordCreateCommand.java index 99e2319d..c359ccd1 100644 --- a/src/main/java/org/runimo/runimo/records/service/usecases/model/RecordCreateCommand.java +++ b/src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordCreateCommand.java @@ -1,10 +1,11 @@ -package org.runimo.runimo.records.service.usecases.model; +package org.runimo.runimo.records.service.usecases.dtos; import org.runimo.runimo.common.scale.Distance; import org.runimo.runimo.common.scale.Pace; -import org.runimo.runimo.records.controller.model.RecordSaveRequest; +import org.runimo.runimo.records.controller.requests.RecordSaveRequest; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; public record RecordCreateCommand( String userPublicId, @@ -23,4 +24,12 @@ public static RecordCreateCommand from(RecordSaveRequest request) { new Distance(request.totalDistanceInMeters()) ); } + + public Long totalDistanceInMeters() { + return totalDistance.getAmount(); + } + + public Long totalDurationInSeconds() { + return ChronoUnit.SECONDS.between(startedAt, endAt); + } } diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/model/RecordDetailViewResponse.java b/src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordDetailViewResponse.java similarity index 93% rename from src/main/java/org/runimo/runimo/records/service/usecases/model/RecordDetailViewResponse.java rename to src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordDetailViewResponse.java index 1eced3a2..51b67a77 100644 --- a/src/main/java/org/runimo/runimo/records/service/usecases/model/RecordDetailViewResponse.java +++ b/src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordDetailViewResponse.java @@ -1,4 +1,4 @@ -package org.runimo.runimo.records.service.usecases.model; +package org.runimo.runimo.records.service.usecases.dtos; import lombok.Builder; import org.runimo.runimo.common.scale.Distance; diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordSaveResponse.java b/src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordSaveResponse.java new file mode 100644 index 00000000..5c2ece5a --- /dev/null +++ b/src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordSaveResponse.java @@ -0,0 +1,5 @@ +package org.runimo.runimo.records.service.usecases.dtos; + +public record RecordSaveResponse(Long savedId +) { +} diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/model/RecordUpdateCommand.java b/src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordUpdateCommand.java similarity index 83% rename from src/main/java/org/runimo/runimo/records/service/usecases/model/RecordUpdateCommand.java rename to src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordUpdateCommand.java index 2cdb0fc7..ba33d5af 100644 --- a/src/main/java/org/runimo/runimo/records/service/usecases/model/RecordUpdateCommand.java +++ b/src/main/java/org/runimo/runimo/records/service/usecases/dtos/RecordUpdateCommand.java @@ -1,4 +1,4 @@ -package org.runimo.runimo.records.service.usecases.model; +package org.runimo.runimo.records.service.usecases.dtos; import lombok.Builder; diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/model/RecordSaveResponse.java b/src/main/java/org/runimo/runimo/records/service/usecases/model/RecordSaveResponse.java deleted file mode 100644 index d5d0e39b..00000000 --- a/src/main/java/org/runimo/runimo/records/service/usecases/model/RecordSaveResponse.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.runimo.runimo.records.service.usecases.model; - -public record RecordSaveResponse(Long savedId -) { -} From 304c3d7e0b251dba917f1fc088e03f2958548605 Mon Sep 17 00:00:00 2001 From: ekgns33 Date: Tue, 25 Mar 2025 10:31:58 +0900 Subject: [PATCH 07/11] =?UTF-8?q?:sparkles:=20feat=20:=20=EB=B3=B4?= =?UTF-8?q?=EC=83=81=20=EC=A7=80=EA=B8=89=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runimo/records/service/RecordFinder.java | 5 +++ .../rewards/RewardHttpResponseCode.java | 41 +++++++++++++++++ .../controller/RunningRewardController.java | 44 +++++++++++++++++++ .../requests/RewardClaimRequest.java | 9 ++++ .../runimo/rewards/service/RewardService.java | 33 ++++++++++++++ .../service/dtos/RewardClaimCommand.java | 7 +++ .../rewards/service/dtos/RewardResponse.java | 9 ++++ .../rewards/service/eggs/EggGrantService.java | 38 ++++++++++++++++ .../user/repository/UserItemRepository.java | 7 +++ .../user/service/UserItemProcessor.java | 23 ++++++++++ 10 files changed, 216 insertions(+) create mode 100644 src/main/java/org/runimo/runimo/rewards/RewardHttpResponseCode.java create mode 100644 src/main/java/org/runimo/runimo/rewards/controller/RunningRewardController.java create mode 100644 src/main/java/org/runimo/runimo/rewards/controller/requests/RewardClaimRequest.java create mode 100644 src/main/java/org/runimo/runimo/rewards/service/RewardService.java create mode 100644 src/main/java/org/runimo/runimo/rewards/service/dtos/RewardClaimCommand.java create mode 100644 src/main/java/org/runimo/runimo/rewards/service/dtos/RewardResponse.java create mode 100644 src/main/java/org/runimo/runimo/rewards/service/eggs/EggGrantService.java create mode 100644 src/main/java/org/runimo/runimo/user/service/UserItemProcessor.java diff --git a/src/main/java/org/runimo/runimo/records/service/RecordFinder.java b/src/main/java/org/runimo/runimo/records/service/RecordFinder.java index 3241fe50..a6ff216d 100644 --- a/src/main/java/org/runimo/runimo/records/service/RecordFinder.java +++ b/src/main/java/org/runimo/runimo/records/service/RecordFinder.java @@ -14,6 +14,11 @@ public class RecordFinder { private final RecordRepository recordRepository; + @Transactional(readOnly = true) + public Optional findById(Long id) { + return recordRepository.findById(id); + } + @Transactional(readOnly = true) public Optional findByPublicId(String id) { return recordRepository.findByRecordPublicId(id); diff --git a/src/main/java/org/runimo/runimo/rewards/RewardHttpResponseCode.java b/src/main/java/org/runimo/runimo/rewards/RewardHttpResponseCode.java new file mode 100644 index 00000000..45179abe --- /dev/null +++ b/src/main/java/org/runimo/runimo/rewards/RewardHttpResponseCode.java @@ -0,0 +1,41 @@ +package org.runimo.runimo.rewards; + +import org.runimo.runimo.exceptions.code.CustomResponseCode; +import org.springframework.http.HttpStatus; + +public enum RewardHttpResponseCode implements CustomResponseCode { + CLAIM_REWARD_SUCESS("RWH2011", "보상 발급 성공", "보상 발급 성공"), + ; + + private final String code; + private final String clientMessage; + private final String logMessage; + + RewardHttpResponseCode(String code, String clientMessage, String logMessage) { + this.code = code; + this.clientMessage = clientMessage; + this.logMessage = logMessage; + } + + @Override + public String getCode() { + return this.code; + } + + @Override + public String getClientMessage() { + return this.clientMessage; + } + + @Override + public String getLogMessage() { + return this.logMessage; + } + + @Override + public HttpStatus getHttpStatusCode() { + return null; + } +} + + diff --git a/src/main/java/org/runimo/runimo/rewards/controller/RunningRewardController.java b/src/main/java/org/runimo/runimo/rewards/controller/RunningRewardController.java new file mode 100644 index 00000000..81bf1325 --- /dev/null +++ b/src/main/java/org/runimo/runimo/rewards/controller/RunningRewardController.java @@ -0,0 +1,44 @@ +package org.runimo.runimo.rewards.controller; + +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.runimo.runimo.common.response.SuccessResponse; +import org.runimo.runimo.rewards.RewardHttpResponseCode; +import org.runimo.runimo.rewards.controller.requests.RewardClaimRequest; +import org.runimo.runimo.rewards.service.RewardService; +import org.runimo.runimo.rewards.service.dtos.RewardClaimCommand; +import org.runimo.runimo.rewards.service.dtos.RewardResponse; +import org.runimo.runimo.user.controller.UserId; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@Tag(name = "REWARD", description = "보상 관련 API") +@RestController +@RequestMapping("/api/v1/rewards/runnings") +@RequiredArgsConstructor +public class RunningRewardController { + + private final RewardService rewardService; + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청"), + @ApiResponse(responseCode = "401", description = "인증 실패"), + @ApiResponse(responseCode = "403", description = "권한 없음"), + @ApiResponse(responseCode = "404", description = "달리기 기록 존재하지 않음.") + }) + @PostMapping + public ResponseEntity> claimRunningReward( + @RequestBody RewardClaimRequest request, + @UserId Long userId + ) { + RewardResponse res = rewardService.claimReward(new RewardClaimCommand(userId, request.recordId())); + return ResponseEntity.ok(SuccessResponse.of(RewardHttpResponseCode.CLAIM_REWARD_SUCESS, res)); + } +} diff --git a/src/main/java/org/runimo/runimo/rewards/controller/requests/RewardClaimRequest.java b/src/main/java/org/runimo/runimo/rewards/controller/requests/RewardClaimRequest.java new file mode 100644 index 00000000..23e344ee --- /dev/null +++ b/src/main/java/org/runimo/runimo/rewards/controller/requests/RewardClaimRequest.java @@ -0,0 +1,9 @@ +package org.runimo.runimo.rewards.controller.requests; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "리워드 클레임 요청 DTO") +public record RewardClaimRequest( + @Schema(description = "보상을 요청하는 달리기 기록 ID", example = "1") Long recordId +) { +} diff --git a/src/main/java/org/runimo/runimo/rewards/service/RewardService.java b/src/main/java/org/runimo/runimo/rewards/service/RewardService.java new file mode 100644 index 00000000..71bb0b61 --- /dev/null +++ b/src/main/java/org/runimo/runimo/rewards/service/RewardService.java @@ -0,0 +1,33 @@ +package org.runimo.runimo.rewards.service; + +import lombok.RequiredArgsConstructor; +import org.runimo.runimo.item.domain.Egg; +import org.runimo.runimo.records.service.RecordFinder; +import org.runimo.runimo.rewards.service.eggs.EggGrantService; +import org.runimo.runimo.rewards.service.dtos.RewardClaimCommand; +import org.runimo.runimo.rewards.service.dtos.RewardResponse; +import org.runimo.runimo.user.domain.User; +import org.runimo.runimo.user.service.UserFinder; +import org.springframework.stereotype.Service; + +import java.util.NoSuchElementException; + +@Service +@RequiredArgsConstructor +public class RewardService { + + private final RecordFinder recordFinder; + private final UserFinder userFinder; + private final EggGrantService eggGrantService; + + public RewardResponse claimReward(RewardClaimCommand command) { + User user = userFinder.findUserById(command.userId()) + .orElseThrow(NoSuchElementException::new); + recordFinder.findById(command.recordId()) + .orElseThrow(NoSuchElementException::new); + // grant egg + Egg grantedEgg = eggGrantService.grantRandomEggToUser(user); + //TODO: 애정 보상 + return new RewardResponse(grantedEgg.getItemCode(), grantedEgg.getEggType()); + } +} diff --git a/src/main/java/org/runimo/runimo/rewards/service/dtos/RewardClaimCommand.java b/src/main/java/org/runimo/runimo/rewards/service/dtos/RewardClaimCommand.java new file mode 100644 index 00000000..06349785 --- /dev/null +++ b/src/main/java/org/runimo/runimo/rewards/service/dtos/RewardClaimCommand.java @@ -0,0 +1,7 @@ +package org.runimo.runimo.rewards.service.dtos; + +public record RewardClaimCommand( + Long userId, + Long recordId +) { +} diff --git a/src/main/java/org/runimo/runimo/rewards/service/dtos/RewardResponse.java b/src/main/java/org/runimo/runimo/rewards/service/dtos/RewardResponse.java new file mode 100644 index 00000000..b9363933 --- /dev/null +++ b/src/main/java/org/runimo/runimo/rewards/service/dtos/RewardResponse.java @@ -0,0 +1,9 @@ +package org.runimo.runimo.rewards.service.dtos; + +import org.runimo.runimo.item.domain.EggType; + +public record RewardResponse( + String eggCode, + EggType eggType +) { +} diff --git a/src/main/java/org/runimo/runimo/rewards/service/eggs/EggGrantService.java b/src/main/java/org/runimo/runimo/rewards/service/eggs/EggGrantService.java new file mode 100644 index 00000000..38d05de3 --- /dev/null +++ b/src/main/java/org/runimo/runimo/rewards/service/eggs/EggGrantService.java @@ -0,0 +1,38 @@ +package org.runimo.runimo.rewards.service.eggs; + +import lombok.RequiredArgsConstructor; +import org.runimo.runimo.item.domain.Egg; +import org.runimo.runimo.item.domain.EggType; +import org.runimo.runimo.item.service.EggFactory; +import org.runimo.runimo.user.domain.User; +import org.runimo.runimo.user.service.UserItemProcessor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class EggGrantService { + + // 회원가입 시 지급하는 알 + private static final Long GREETING_EGG_AMOUNT = 1L; + private static final Long DEFAULT_REWARD_EGG_AMOUNT = 1L; + private final EggFactory eggFactory; + private final UserItemProcessor userItemProcessor; + + @Transactional + public void grantGreetingEggToUser(User user) { + if (!user.checkUserFirstRun()) return; + Egg grantedEgg = eggFactory.createGreetingEgg(); + userItemProcessor.updateItemQuantity(user.getId(), grantedEgg.getId(), GREETING_EGG_AMOUNT); + } + + @Transactional + public Egg grantRandomEggToUser(User user) { + List unLockedEggTypes = EggType.getUnLockedEggTypes(user.getTotalDistanceInMeters()); + Egg grantedEgg = eggFactory.createEggRandomly(unLockedEggTypes); + userItemProcessor.updateItemQuantity(user.getId(), grantedEgg.getId(), DEFAULT_REWARD_EGG_AMOUNT); + return grantedEgg; + } +} diff --git a/src/main/java/org/runimo/runimo/user/repository/UserItemRepository.java b/src/main/java/org/runimo/runimo/user/repository/UserItemRepository.java index 90ea7483..592bafe6 100644 --- a/src/main/java/org/runimo/runimo/user/repository/UserItemRepository.java +++ b/src/main/java/org/runimo/runimo/user/repository/UserItemRepository.java @@ -2,6 +2,7 @@ import jakarta.persistence.LockModeType; import jakarta.persistence.QueryHint; +import org.runimo.runimo.item.domain.EggType; import org.runimo.runimo.user.domain.UserItem; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Lock; @@ -20,4 +21,10 @@ public interface UserItemRepository extends JpaRepository { @QueryHints({@QueryHint(name = "jakarta.persistence.lock.timeout", value = "3000")}) @Query("select ui from UserItem ui where ui.userId = :userId and ui.itemId = :itemId") Optional findByUserIdAndItemIdForUpdate(Long userId, Long itemId); + + @Query("select ui " + + "from UserItem ui " + + "join Egg e on ui.itemId = e.id " + + "where ui.userId = :userId and e.eggType = :eggType") + Optional findByUserIdAndEggType(Long userId, EggType eggType); } diff --git a/src/main/java/org/runimo/runimo/user/service/UserItemProcessor.java b/src/main/java/org/runimo/runimo/user/service/UserItemProcessor.java new file mode 100644 index 00000000..f914f569 --- /dev/null +++ b/src/main/java/org/runimo/runimo/user/service/UserItemProcessor.java @@ -0,0 +1,23 @@ +package org.runimo.runimo.user.service; + +import lombok.RequiredArgsConstructor; +import org.runimo.runimo.user.domain.UserItem; +import org.runimo.runimo.user.repository.UserItemRepository; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +@RequiredArgsConstructor +public class UserItemProcessor { + + private final UserItemFinder userItemFinder; + private final UserItemRepository userItemRepository; + + @Transactional + public void updateItemQuantity(Long userId, Long itemId, Long amount) { + UserItem userItem = userItemFinder.findByUserIdAndItemId(userId, itemId) + .orElseThrow(IllegalStateException::new); + userItem.gainItem(amount); + userItemRepository.save(userItem); + } +} From 40d90e1ceab6e48604d934908d6c5570313c9770 Mon Sep 17 00:00:00 2001 From: ekgns33 Date: Tue, 25 Mar 2025 10:33:40 +0900 Subject: [PATCH 08/11] =?UTF-8?q?:sparkles:=20feat=20:=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runimo/user/service/UserItemCreator.java | 6 ++-- .../runimo/user/service/UserItemFinder.java | 11 +++++-- .../runimo/user/service/UserStatService.java | 25 ++++++++++++++++ .../service/usecases/GainItemUsecaseImpl.java | 2 +- .../usecases/UserOAuthUsecaseImpl.java | 10 ++----- .../service/usecases/UserRegisterService.java | 29 +++++++++++++++++++ 6 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/runimo/runimo/user/service/UserStatService.java create mode 100644 src/main/java/org/runimo/runimo/user/service/usecases/UserRegisterService.java diff --git a/src/main/java/org/runimo/runimo/user/service/UserItemCreator.java b/src/main/java/org/runimo/runimo/user/service/UserItemCreator.java index 8204c53d..dc3f94f7 100644 --- a/src/main/java/org/runimo/runimo/user/service/UserItemCreator.java +++ b/src/main/java/org/runimo/runimo/user/service/UserItemCreator.java @@ -22,9 +22,9 @@ public UserItem create(UserItem userItem) { } /* - * 아이템ID, 유저ID의 모든 순서쌍을 저장한다. - * 회원가입 시 실행된다. - * */ + * 아이템ID, 유저ID의 모든 순서쌍을 저장한다. + * 회원가입 시 실행된다. + * */ @Transactional public void createAll(Long userId) { List itemIds = itemRepository.findAllItemIds(); diff --git a/src/main/java/org/runimo/runimo/user/service/UserItemFinder.java b/src/main/java/org/runimo/runimo/user/service/UserItemFinder.java index bf4b753d..4b9cea37 100644 --- a/src/main/java/org/runimo/runimo/user/service/UserItemFinder.java +++ b/src/main/java/org/runimo/runimo/user/service/UserItemFinder.java @@ -1,6 +1,7 @@ package org.runimo.runimo.user.service; import lombok.RequiredArgsConstructor; +import org.runimo.runimo.item.domain.EggType; import org.runimo.runimo.user.domain.UserItem; import org.runimo.runimo.user.repository.UserItemRepository; import org.springframework.stereotype.Component; @@ -14,12 +15,18 @@ public class UserItemFinder { private final UserItemRepository userItemRepository; + @Transactional(readOnly = true) + public Optional findEggByUserIdAndEggType(Long userId, EggType eggType) { + return userItemRepository.findByUserIdAndEggType(userId, eggType); + } + @Transactional(readOnly = true) public Optional findByUserIdAndItemId(Long userId, Long itemId) { - return userItemRepository.findByUserIdAndItemId(userId,itemId); + return userItemRepository.findByUserIdAndItemId(userId, itemId); } + @Transactional public Optional findByUserIdAndItemIdWithXLock(Long userId, Long itemId) { - return userItemRepository.findByUserIdAndItemIdForUpdate(userId,itemId); + return userItemRepository.findByUserIdAndItemIdForUpdate(userId, itemId); } } diff --git a/src/main/java/org/runimo/runimo/user/service/UserStatService.java b/src/main/java/org/runimo/runimo/user/service/UserStatService.java new file mode 100644 index 00000000..065bb14a --- /dev/null +++ b/src/main/java/org/runimo/runimo/user/service/UserStatService.java @@ -0,0 +1,25 @@ +package org.runimo.runimo.user.service; + +import lombok.RequiredArgsConstructor; +import org.runimo.runimo.records.service.usecases.dtos.RecordCreateCommand; +import org.runimo.runimo.user.domain.User; +import org.runimo.runimo.user.repository.UserRepository; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +@RequiredArgsConstructor +public class UserStatService { + + private final UserRepository userStatRepository; + + @Transactional + public void updateUserStats(User user, RecordCreateCommand command) { + user.updateRunningStats( + command.totalDistanceInMeters(), + command.totalDurationInSeconds() + ); + userStatRepository.save(user); + } + +} diff --git a/src/main/java/org/runimo/runimo/user/service/usecases/GainItemUsecaseImpl.java b/src/main/java/org/runimo/runimo/user/service/usecases/GainItemUsecaseImpl.java index ccd6a653..f4f810e3 100644 --- a/src/main/java/org/runimo/runimo/user/service/usecases/GainItemUsecaseImpl.java +++ b/src/main/java/org/runimo/runimo/user/service/usecases/GainItemUsecaseImpl.java @@ -22,7 +22,7 @@ public class GainItemUsecaseImpl implements GainItemUsecase { @Transactional public GainItemResponse gainItem(GainItemCommand command) { UserItem userItem = userItemFinder.findByUserIdAndItemIdWithXLock(command.userId(), command.itemId()) - .orElseThrow(NoSuchElementException::new); + .orElseThrow(NoSuchElementException::new); userItem.gainItem(command.quantity()); userItemRepository.save(userItem); return new GainItemResponse( diff --git a/src/main/java/org/runimo/runimo/user/service/usecases/UserOAuthUsecaseImpl.java b/src/main/java/org/runimo/runimo/user/service/usecases/UserOAuthUsecaseImpl.java index 748f50a6..6b2b150c 100644 --- a/src/main/java/org/runimo/runimo/user/service/usecases/UserOAuthUsecaseImpl.java +++ b/src/main/java/org/runimo/runimo/user/service/usecases/UserOAuthUsecaseImpl.java @@ -11,8 +11,6 @@ import org.runimo.runimo.user.domain.SocialProvider; import org.runimo.runimo.user.domain.User; import org.runimo.runimo.user.repository.OAuthInfoRepository; -import org.runimo.runimo.user.service.UserCreator; -import org.runimo.runimo.user.service.UserItemCreator; import org.runimo.runimo.user.service.dtos.SignupUserInfo; import org.runimo.runimo.user.service.dtos.TokenPair; import org.runimo.runimo.user.service.dtos.UserSignupCommand; @@ -26,9 +24,8 @@ public class UserOAuthUsecaseImpl implements UserOAuthUsecase { private final JwtTokenFactory jwtfactory; private final OidcService oidcService; private final OidcNonceService oidcNonceService; - private final UserItemCreator userItemCreator; private final OAuthInfoRepository oAuthInfoRepository; - private final UserCreator userCreator; + private final UserRegisterService userRegisterService; @Override @Transactional @@ -50,10 +47,7 @@ public SignupUserInfo validateAndSignup(final UserSignupCommand command, final S .ifPresent(oAuthInfo -> { throw new IllegalArgumentException(); }); - - User savedUser = userCreator.createUser(command); - userCreator.createUserOAuthInfo(savedUser, provider, pid); - userItemCreator.createAll(savedUser.getId()); + User savedUser = userRegisterService.register(command, provider, pid); return new SignupUserInfo(savedUser.getId(), jwtfactory.generateTokenPair(savedUser)); } } diff --git a/src/main/java/org/runimo/runimo/user/service/usecases/UserRegisterService.java b/src/main/java/org/runimo/runimo/user/service/usecases/UserRegisterService.java new file mode 100644 index 00000000..20c146dc --- /dev/null +++ b/src/main/java/org/runimo/runimo/user/service/usecases/UserRegisterService.java @@ -0,0 +1,29 @@ +package org.runimo.runimo.user.service.usecases; + +import lombok.RequiredArgsConstructor; +import org.runimo.runimo.rewards.service.eggs.EggGrantService; +import org.runimo.runimo.user.domain.SocialProvider; +import org.runimo.runimo.user.domain.User; +import org.runimo.runimo.user.service.UserCreator; +import org.runimo.runimo.user.service.UserItemCreator; +import org.runimo.runimo.user.service.dtos.UserSignupCommand; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +@RequiredArgsConstructor +public class UserRegisterService { + + private final UserCreator userCreator; + private final UserItemCreator userItemCreator; + private final EggGrantService eggGrantService; + + @Transactional + public User register(UserSignupCommand command, SocialProvider provider, String providerId) { + User savedUser = userCreator.createUser(command); + userCreator.createUserOAuthInfo(savedUser, provider, providerId); + userItemCreator.createAll(savedUser.getId()); + eggGrantService.grantGreetingEggToUser(savedUser); + return savedUser; + } +} From faac643ec28478e0b2db6d59234fb177035ec20b Mon Sep 17 00:00:00 2001 From: ekgns33 Date: Tue, 25 Mar 2025 10:33:51 +0900 Subject: [PATCH 09/11] =?UTF-8?q?:sparkles:=20feat=20:=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/runimo/runimo/common/scale/Distance.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/runimo/runimo/common/scale/Distance.java b/src/main/java/org/runimo/runimo/common/scale/Distance.java index dc87e496..d77ce1b1 100644 --- a/src/main/java/org/runimo/runimo/common/scale/Distance.java +++ b/src/main/java/org/runimo/runimo/common/scale/Distance.java @@ -17,4 +17,8 @@ public class Distance implements Serializable { public Distance(Long amount) { this.amount = amount; } + + public Long getAmount() { + return amount; + } } From 5024d5a4bc936397711cae727eccab93930181aa Mon Sep 17 00:00:00 2001 From: ekgns33 Date: Tue, 25 Mar 2025 10:35:01 +0900 Subject: [PATCH 10/11] =?UTF-8?q?:white=5Fcheck=5Fmark:=20test=20:=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/EggTypeRandomGeneratorTest.java | 25 ++++++ .../org/runimo/runimo/rewards/RewardTest.java | 83 +++++++++++++++++++ .../runimo/user/domain/UserItemTest.java | 2 +- .../service/usecases/UseItemUsecaseTest.java | 3 +- .../usecases/UserRegisterServiceTest.java | 38 +++++++++ 5 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/runimo/runimo/item/service/EggTypeRandomGeneratorTest.java create mode 100644 src/test/java/org/runimo/runimo/rewards/RewardTest.java create mode 100644 src/test/java/org/runimo/runimo/user/service/usecases/UserRegisterServiceTest.java diff --git a/src/test/java/org/runimo/runimo/item/service/EggTypeRandomGeneratorTest.java b/src/test/java/org/runimo/runimo/item/service/EggTypeRandomGeneratorTest.java new file mode 100644 index 00000000..c1ec7da3 --- /dev/null +++ b/src/test/java/org/runimo/runimo/item/service/EggTypeRandomGeneratorTest.java @@ -0,0 +1,25 @@ +package org.runimo.runimo.item.service; + +import org.junit.jupiter.api.Test; +import org.runimo.runimo.item.domain.EggType; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class EggTypeRandomGeneratorTest { + + + private final EggTypeRandomGenerator eggTypeRandomGenerator = new EggTypeRandomGenerator(); + + @Test + void generateRandomEggType() { + //given + Long totalRunningDistanceInMeters = 34444L; + //when + for (int i = 0; i < 10; i++) { + EggType selectedType = eggTypeRandomGenerator.generateRandomEggType( + EggType.getUnLockedEggTypes(totalRunningDistanceInMeters)); + assertNotNull(selectedType); + } + } + +} \ No newline at end of file diff --git a/src/test/java/org/runimo/runimo/rewards/RewardTest.java b/src/test/java/org/runimo/runimo/rewards/RewardTest.java new file mode 100644 index 00000000..15a8b662 --- /dev/null +++ b/src/test/java/org/runimo/runimo/rewards/RewardTest.java @@ -0,0 +1,83 @@ +package org.runimo.runimo.rewards; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.runimo.runimo.CleanUpUtil; +import org.runimo.runimo.common.scale.Distance; +import org.runimo.runimo.common.scale.Pace; +import org.runimo.runimo.records.service.usecases.RecordCreateUsecase; +import org.runimo.runimo.records.service.usecases.dtos.RecordCreateCommand; +import org.runimo.runimo.records.service.usecases.dtos.RecordSaveResponse; +import org.runimo.runimo.rewards.service.RewardService; +import org.runimo.runimo.rewards.service.dtos.RewardClaimCommand; +import org.runimo.runimo.rewards.service.dtos.RewardResponse; +import org.runimo.runimo.user.domain.SocialProvider; +import org.runimo.runimo.user.domain.User; +import org.runimo.runimo.user.domain.UserItem; +import org.runimo.runimo.user.service.UserItemFinder; +import org.runimo.runimo.user.service.dtos.UserSignupCommand; +import org.runimo.runimo.user.service.usecases.UserRegisterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@ActiveProfiles("test") +@SpringBootTest +class RewardTest { + + @Autowired + private UserRegisterService userRegisterService; + @Autowired + private RewardService rewardService; + + @Autowired + private RecordCreateUsecase recordCreateUsecase; + + private User savedUser; + @Autowired + private UserItemFinder userItemFinder; + @Autowired + private CleanUpUtil cleanUpUtil; + + @BeforeEach + void setUp() { + //given + UserSignupCommand command = new UserSignupCommand("test", SocialProvider.KAKAO, "1234"); + savedUser = userRegisterService.register(command, SocialProvider.KAKAO, "1234"); + } + + @AfterEach + void tearDown() { + cleanUpUtil.cleanUpUserInfos(); + } + + @Test + void 보상_요청_테스트() { + RecordCreateCommand recordCreateCommand = new RecordCreateCommand( + savedUser.getPublicId(), + LocalDateTime.now(), + LocalDateTime.now().plusHours(1), + new Pace(1909L), + new Distance(10000L) + ); + RecordSaveResponse response = recordCreateUsecase.execute(recordCreateCommand); + RewardClaimCommand rewardClaimCommand = new RewardClaimCommand(savedUser.getId(), response.savedId()); + RewardResponse rewardResponse = rewardService.claimReward(rewardClaimCommand); + + UserItem savedItem = userItemFinder. + findEggByUserIdAndEggType( + savedUser.getId(), + rewardResponse.eggType()) + .get(); + + assertNotNull(rewardResponse.eggCode()); + assertNotNull(savedItem); + assertNotEquals(0, savedItem.getQuantity()); + } +} diff --git a/src/test/java/org/runimo/runimo/user/domain/UserItemTest.java b/src/test/java/org/runimo/runimo/user/domain/UserItemTest.java index 77a94999..e9d1958d 100644 --- a/src/test/java/org/runimo/runimo/user/domain/UserItemTest.java +++ b/src/test/java/org/runimo/runimo/user/domain/UserItemTest.java @@ -26,7 +26,7 @@ class UserItemTest { UserItem userItem = UserItemFixtures.getUserItemWithQuantity(10L); //when - assertThrows(IllegalArgumentException.class, ()-> userItem.useItem(20L)); + assertThrows(IllegalArgumentException.class, () -> userItem.useItem(20L)); assertEquals(10L, userItem.getQuantity()); } diff --git a/src/test/java/org/runimo/runimo/user/service/usecases/UseItemUsecaseTest.java b/src/test/java/org/runimo/runimo/user/service/usecases/UseItemUsecaseTest.java index 790712ac..004c38c0 100644 --- a/src/test/java/org/runimo/runimo/user/service/usecases/UseItemUsecaseTest.java +++ b/src/test/java/org/runimo/runimo/user/service/usecases/UseItemUsecaseTest.java @@ -13,7 +13,8 @@ import java.util.Optional; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; diff --git a/src/test/java/org/runimo/runimo/user/service/usecases/UserRegisterServiceTest.java b/src/test/java/org/runimo/runimo/user/service/usecases/UserRegisterServiceTest.java new file mode 100644 index 00000000..953476de --- /dev/null +++ b/src/test/java/org/runimo/runimo/user/service/usecases/UserRegisterServiceTest.java @@ -0,0 +1,38 @@ +package org.runimo.runimo.user.service.usecases; + +import org.junit.jupiter.api.Test; +import org.runimo.runimo.item.repository.ItemRepository; +import org.runimo.runimo.user.domain.SocialProvider; +import org.runimo.runimo.user.domain.User; +import org.runimo.runimo.user.domain.UserItem; +import org.runimo.runimo.user.repository.UserItemRepository; +import org.runimo.runimo.user.service.dtos.UserSignupCommand; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@ActiveProfiles("test") +@SpringBootTest +class UserRegisterServiceTest { + + @Autowired + private UserRegisterService userRegisterService; + + @Autowired + private UserItemRepository userItemRepository; + @Autowired + private ItemRepository itemRepository; + + @Test + void 회원가입_알_지급_테스트() { + // given + UserSignupCommand command = new UserSignupCommand("test", SocialProvider.KAKAO, "1234"); + User createdUser = userRegisterService.register(command, SocialProvider.KAKAO, "1234"); + UserItem ui = userItemRepository.findByUserIdAndItemId(createdUser.getId(), 1L).get(); + assertNotNull(ui); + assertEquals(1L, ui.getQuantity()); + } +} \ No newline at end of file From 4429396b58e92d3841dab2e7f4ae0e88889630b6 Mon Sep 17 00:00:00 2001 From: ekgns33 Date: Tue, 25 Mar 2025 10:35:13 +0900 Subject: [PATCH 11/11] =?UTF-8?q?:white=5Fcheck=5Fmark:=20test=20:=20?= =?UTF-8?q?=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EC=A0=95=EB=A6=AC=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/runimo/runimo/CleanUpUtil.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/test/java/org/runimo/runimo/CleanUpUtil.java diff --git a/src/test/java/org/runimo/runimo/CleanUpUtil.java b/src/test/java/org/runimo/runimo/CleanUpUtil.java new file mode 100644 index 00000000..460aa398 --- /dev/null +++ b/src/test/java/org/runimo/runimo/CleanUpUtil.java @@ -0,0 +1,25 @@ +package org.runimo.runimo; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +@Component +public class CleanUpUtil { + + private static final String[] USER_TABLES = { + "user_item", + "oauth_accounts", + "users" + }; + + @Autowired + private JdbcTemplate jdbcTemplate; + + public void cleanUpUserInfos() { + for (String table : USER_TABLES) { + jdbcTemplate.execute("DELETE FROM " + table); + jdbcTemplate.update("ALTER TABLE " + table + " ALTER COLUMN id RESTART WITH 1"); + } + } +}