Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] sologame 기록 저장 #27

Merged
merged 11 commits into from
Sep 29, 2024
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'
compileOnly 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ku.user.domain.Ranking.controller;

import ku.user.domain.Ranking.domain.RhythmScore;
import ku.user.domain.Ranking.domain.SteppingStonesScore;
import ku.user.domain.Ranking.dto.request.SaveRhythmRequest;
import ku.user.domain.Ranking.dto.request.SaveSteppingRequest;
import ku.user.domain.Ranking.dto.response.SaveRhythmResponse;
import ku.user.domain.Ranking.dto.response.SaveSteppingResponse;
import ku.user.domain.Ranking.service.RhythmScoreService;
import ku.user.global.response.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class RhythmScoreController {
private final RhythmScoreService rhythmScoreService;

// 저장
@PostMapping("/rhythm")
Copy link
Member

Choose a reason for hiding this comment

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

restful API에서는 rhythms으로 하지 않나요?

Copy link
Member Author

Choose a reason for hiding this comment

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

그게 더 맞을거 같네요

public ApiResponse<SaveRhythmResponse> saveStepping(@RequestBody SaveRhythmRequest saveSteppingRequest){
RhythmScore rhythmScore = SaveRhythmRequest.toEntity(saveSteppingRequest);
RhythmScore savedScore = rhythmScoreService.saveScore(rhythmScore);
SaveRhythmResponse response = SaveRhythmResponse.fromEntity(savedScore);

return new ApiResponse<>(true, response, null);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ku.user.domain.Ranking.controller;

import ku.user.domain.Ranking.domain.SteppingStonesScore;
import ku.user.domain.Ranking.dto.request.SaveSteppingRequest;
import ku.user.domain.Ranking.dto.response.SaveSteppingResponse;
import ku.user.domain.Ranking.service.SteppingStonesScoreService;
import ku.user.global.response.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class SteppingStonesScoreController {
private final SteppingStonesScoreService steppingStonesScoreService;
// 저장
@PostMapping("/stepping")
public ApiResponse<SaveSteppingResponse> saveStepping(@RequestBody SaveSteppingRequest saveSteppingRequest){
SteppingStonesScore steppingStonesScore = SaveSteppingRequest.toEntity(saveSteppingRequest);
SteppingStonesScore savedScore = steppingStonesScoreService.saveScore(steppingStonesScore);
SaveSteppingResponse response = SaveSteppingResponse.fromEntity(savedScore);

return new ApiResponse<>(true, response, null);
}
}
35 changes: 35 additions & 0 deletions src/main/java/ku/user/domain/Ranking/domain/RhythmScore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ku.user.domain.Ranking.domain;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;
// 점수, 캐릭터명, 날짜, 재화
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@Getter
@Table(name = "rhythm_scores")
public class RhythmScore {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "nickname", nullable = false, length = 50)
private String nickName;

@Column(nullable = false)
private int score;

@Column(name = "created_at", nullable = false, updatable = false)
@CreationTimestamp
Copy link
Member

Choose a reason for hiding this comment

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

이건 처음 보네요

private LocalDateTime createdAt;

@Setter
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
private Status status = Status.ACTIVE;

}
17 changes: 17 additions & 0 deletions src/main/java/ku/user/domain/Ranking/domain/Status.java
Copy link
Member

Choose a reason for hiding this comment

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

1주일에 대한 랭킹 제공이라면 게임 전적에 대한 랭킹 포함 여부를 결정하는 enum으로 이해했어요

Copy link
Member Author

Choose a reason for hiding this comment

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

저거는 사용자가 캐릭터를 삭제할 경우 기록에서 표시하기 위한 필드였습니다. 랭킹에 대해서는 고민하고 있는 부분이 있는데 회의에서 이야기 하도록 하겠습니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ku.user.domain.Ranking.domain;

public enum Status {
ACTIVE("활성화"),
INACTIVE("비활성화");

private final String description;

Status(String description) {
this.description = description;
}

public String getDescription() {
return description;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package ku.user.domain.Ranking.domain;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@Getter
@Table(name = "stepping_stones_score")
public class SteppingStonesScore {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "nickname", nullable = false, length = 50)
private String nickName;

@Column(nullable = false)
private int score;

@Column(nullable = false)
private int coin;

@Column(name = "created_at", nullable = false, updatable = false)
@CreationTimestamp
private LocalDateTime createdAt;

@Setter
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
private Status status = Status.ACTIVE;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ku.user.domain.Ranking.dto.request;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import ku.user.domain.Ranking.domain.RhythmScore;
import ku.user.domain.Ranking.domain.Status;
import lombok.Getter;

@Getter
public class SaveRhythmRequest {
@NotBlank(message = "닉네임은 필수 항목")
private String nickName;

@Min(value = 0, message = "점수는 0 이상이어야 합니다.")
private int score;


public static RhythmScore toEntity(SaveRhythmRequest saveRhythmRequest){
return RhythmScore.builder()
.nickName(saveRhythmRequest.getNickName())
.score(saveRhythmRequest.getScore())
.status(Status.ACTIVE)
.build();

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ku.user.domain.Ranking.dto.request;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import ku.user.domain.Ranking.domain.Status;
import ku.user.domain.Ranking.domain.SteppingStonesScore;
import lombok.Getter;

@Getter
public class SaveSteppingRequest {
@NotBlank(message = "닉네임은 필수 항목")
private String nickName;

@Min(value = 0, message = "점수는 0 이상이어야 합니다.")
private int score;

@Min(value = 0, message = "코인은 0 이상이어야 합니다.")
private int coin;

public static SteppingStonesScore toEntity(SaveSteppingRequest saveSteppingRequest){
return SteppingStonesScore.builder()
.nickName(saveSteppingRequest.getNickName())
.score(saveSteppingRequest.getScore())
.coin(saveSteppingRequest.getCoin())
.status(Status.ACTIVE)
.build();

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ku.user.domain.Ranking.dto.response;

import ku.user.domain.Ranking.domain.RhythmScore;
import ku.user.domain.Ranking.domain.SteppingStonesScore;

public record SaveRhythmResponse(Long id, String nickName, int score) {
public static SaveRhythmResponse fromEntity(RhythmScore rhythmScore) {
return new SaveRhythmResponse(
rhythmScore.getId(),
rhythmScore.getNickName(),
rhythmScore.getScore()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ku.user.domain.Ranking.dto.response;

import ku.user.domain.Ranking.domain.SteppingStonesScore;

public record SaveSteppingResponse(Long id, String nickName, int score, int coin) {
public static SaveSteppingResponse fromEntity(SteppingStonesScore steppingStonesScore) {
return new SaveSteppingResponse(
steppingStonesScore.getId(),
steppingStonesScore.getNickName(),
steppingStonesScore.getScore(),
steppingStonesScore.getCoin()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ku.user.domain.Ranking.infrastructure;

import ku.user.domain.Ranking.domain.RhythmScore;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Repository
public interface RhythmScoreRepository extends JpaRepository<RhythmScore,Long> {
List<RhythmScore> findRhythmScoresByNickName(String nickname);

Optional<RhythmScore> findTopByNickNameOrderByScoreDesc(String nickName);

List<RhythmScore> findByNickNameAndCreatedAtBetween(String nickName, LocalDateTime startDate, LocalDateTime endDate);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ku.user.domain.Ranking.infrastructure;

import ku.user.domain.Ranking.domain.RhythmScore;
import ku.user.domain.Ranking.domain.SteppingStonesScore;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Repository
public interface SteppingScoreRepository extends JpaRepository<SteppingStonesScore,Long> {
List<SteppingStonesScore> findSteppingStonesScoresByNickName(String nickname);

Optional<SteppingStonesScore> findTopByNickNameOrderByScoreDesc(String nickName);

List<SteppingStonesScore> findByNickNameAndCreatedAtBetween(String nickName, LocalDateTime startDate, LocalDateTime endDate);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package ku.user.domain.Ranking.service;

import ku.user.domain.Ranking.domain.RhythmScore;
import ku.user.domain.Ranking.domain.Status;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

public interface RhythmScoreService {
// 생성
RhythmScore saveScore(RhythmScore rhythmScore);

// 조회(해당 캐릭터의 모든 기록)
List<RhythmScore> findScoresByNickName(String nickName);

// 조회(해당 캐릭터의 특정 날짜 기록)
List<RhythmScore> findScoresByNickNameAndDate(String nickName, LocalDateTime startDate, LocalDateTime endDate);

// 캐릭터 삭제
void updateScoreStatus(String nickName);

// 해당 캐릭터의 최고 기록
RhythmScore findHighestScoreByNickName(String nickName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package ku.user.domain.Ranking.service;

import ku.user.domain.Ranking.domain.RhythmScore;
import ku.user.domain.Ranking.domain.Status;
import ku.user.domain.Ranking.infrastructure.RhythmScoreRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class RhythmScoreServiceImpl implements RhythmScoreService{
private final RhythmScoreRepository repository;

@Transactional
public RhythmScore saveScore(RhythmScore rhythmScore) {
return repository.save(rhythmScore);
}


public List<RhythmScore> findScoresByNickName(String nickName) {
List<RhythmScore> scores = repository.findRhythmScoresByNickName(nickName);
return scores;
}


public List<RhythmScore> findScoresByNickNameAndDate(String nickName, LocalDateTime startDate, LocalDateTime endDate) {
return repository.findByNickNameAndCreatedAtBetween(nickName, startDate, endDate);
}

@Transactional
public void updateScoreStatus(String nickName) {
List<RhythmScore> scores = repository.findRhythmScoresByNickName(nickName);

if (scores.isEmpty()) {
throw new IllegalArgumentException("해당 닉네임의 기록은 존재하지 않음: " + nickName);
}

boolean hasActiveRecord = false;
for (RhythmScore score : scores) {
if (score.getStatus() == Status.ACTIVE) {
score.setStatus(Status.INACTIVE);
hasActiveRecord = true;
}
}

if (!hasActiveRecord) {
System.out.println("이미 비활성화 되어 있는 캐릭터입니다.");
return;
}

repository.saveAll(scores);
}


public RhythmScore findHighestScoreByNickName(String nickName) {
Optional<RhythmScore> optionalScore = repository.findTopByNickNameOrderByScoreDesc(nickName);
return optionalScore.orElse(null);
}
}
Loading
Loading