Skip to content

Commit e4a7acc

Browse files
๐Ÿ“ Add docstrings to feature/rythm8beat
Docstrings generation was requested by @smpringles24. * #308 (comment) The following files were modified: * `src/main/java/inha/gdgoc/domain/game/controller/Rythm8beatScoreController.java` * `src/main/java/inha/gdgoc/domain/game/entity/Rythm8beatScore.java` * `src/main/java/inha/gdgoc/domain/game/repository/Rythm8beatScoreRepository.java` * `src/main/java/inha/gdgoc/domain/game/service/Rythm8beatScoreService.java` * `src/main/java/inha/gdgoc/global/security/SecurityConfig.java`
1 parent 938474f commit e4a7acc

5 files changed

Lines changed: 240 additions & 1 deletion

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package inha.gdgoc.domain.game.controller;
2+
3+
import static inha.gdgoc.domain.game.controller.message.Rythm8beatScoreMessage.RANKING_RETRIEVED;
4+
import static inha.gdgoc.domain.game.controller.message.Rythm8beatScoreMessage.SCORES_RESET;
5+
import static inha.gdgoc.domain.game.controller.message.Rythm8beatScoreMessage.SCORE_SUBMITTED;
6+
7+
import inha.gdgoc.domain.game.dto.request.Rythm8beatScoreRequest;
8+
import inha.gdgoc.domain.game.dto.response.Rythm8beatRankingResponse;
9+
import inha.gdgoc.domain.game.service.Rythm8beatScoreService;
10+
import inha.gdgoc.global.dto.response.ApiResponse;
11+
import jakarta.validation.Valid;
12+
import lombok.RequiredArgsConstructor;
13+
import org.springframework.http.ResponseEntity;
14+
import org.springframework.web.bind.annotation.DeleteMapping;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.PostMapping;
17+
import org.springframework.web.bind.annotation.RequestBody;
18+
import org.springframework.web.bind.annotation.RequestMapping;
19+
import org.springframework.web.bind.annotation.RequestParam;
20+
import org.springframework.web.bind.annotation.RestController;
21+
22+
@RequestMapping("/api/v1/game/rythm8beat")
23+
@RestController
24+
@RequiredArgsConstructor
25+
public class Rythm8beatScoreController {
26+
27+
private final Rythm8beatScoreService rythm8beatScoreService;
28+
29+
/**
30+
* ๋ฆฌ๋“ฌ8๋น„ํŠธ ์ ์ˆ˜ ์ œ์ถœ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.
31+
*
32+
* @param request ์ œ์ถœํ•  ์ ์ˆ˜ ์ •๋ณด๋ฅผ ๋‹ด์€ ์š”์ฒญ ๋ณธ๋ฌธ(์œ ํšจ์„ฑ ๊ฒ€์ฆ๋จ)
33+
* @return ์„ฑ๊ณต ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋‹ด์€ ApiResponse. ํŽ˜์ด๋กœ๋“œ๋Š” ๋น„์–ด์žˆ๊ณ  ๋ฉ”์‹œ์ง€๋Š” SCORE_SUBMITTED
34+
*/
35+
@PostMapping("/scores")
36+
public ResponseEntity<ApiResponse<Void, Void>> submitScore(
37+
@Valid @RequestBody Rythm8beatScoreRequest request) {
38+
rythm8beatScoreService.submitScore(request);
39+
return ResponseEntity.ok(ApiResponse.ok(SCORE_SUBMITTED));
40+
}
41+
42+
/**
43+
* ๋ฆฌ๋“ฌ8๋น„ํŠธ ๊ฒŒ์ž„์˜ ์ „์ฒด ๋žญํ‚น ๋ฐ (์„ ํƒ์ ์œผ๋กœ) ํŠน์ • ์‚ฌ์šฉ์ž ๋žญํ‚น์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
44+
*
45+
* @param phoneNumber ์กฐํšŒํ•  ์‚ฌ์šฉ์ž์˜ ์ „ํ™”๋ฒˆํ˜ธ(์„ ํƒ). ์ œ๊ณตํ•˜๋ฉด ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ์ˆœ์œ„ ์ •๋ณด๊ฐ€ ์‘๋‹ต์— ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.
46+
* @return ๋žญํ‚น ์ •๋ณด๋ฅผ ๋‹ด์€ Rythm8beatRankingResponse๋ฅผ ํŽ˜์ด๋กœ๋“œ๋กœ ํฌํ•จํ•˜๋Š” ApiResponse๋ฅผ ๋‹ด์€ ResponseEntity
47+
*/
48+
@GetMapping("/ranking")
49+
public ResponseEntity<ApiResponse<Rythm8beatRankingResponse, Void>> getRanking(
50+
@RequestParam(required = false) String phoneNumber) {
51+
Rythm8beatRankingResponse response = rythm8beatScoreService.getRanking(phoneNumber);
52+
return ResponseEntity.ok(ApiResponse.ok(RANKING_RETRIEVED, response));
53+
}
54+
55+
/**
56+
* ๋ชจ๋“  ๋ฆฌ๋“ฌ8๋น„ํŠธ ์ ์ˆ˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
57+
*
58+
* @return ApiResponse์— ๋นˆ ํŽ˜์ด๋กœ๋“œ์™€ SCORES_RESET ๋ฉ”์‹œ์ง€๋ฅผ ํฌํ•จํ•œ HTTP 200 OK ์‘๋‹ต
59+
*/
60+
@DeleteMapping("/scores/all")
61+
public ResponseEntity<ApiResponse<Void, Void>> resetAll() {
62+
rythm8beatScoreService.resetAll();
63+
return ResponseEntity.ok(ApiResponse.ok(SCORES_RESET));
64+
}
65+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package inha.gdgoc.domain.game.entity;
2+
3+
import inha.gdgoc.global.entity.BaseEntity;
4+
import jakarta.persistence.Column;
5+
import jakarta.persistence.Entity;
6+
import jakarta.persistence.GeneratedValue;
7+
import jakarta.persistence.GenerationType;
8+
import jakarta.persistence.Id;
9+
import jakarta.persistence.Table;
10+
import lombok.AccessLevel;
11+
import lombok.AllArgsConstructor;
12+
import lombok.Builder;
13+
import lombok.Getter;
14+
import lombok.NoArgsConstructor;
15+
16+
@Entity
17+
@Table(name = "game_scores")
18+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
19+
@AllArgsConstructor
20+
@Getter
21+
@Builder
22+
public class Rythm8beatScore extends BaseEntity {
23+
24+
@Id
25+
@GeneratedValue(strategy = GenerationType.IDENTITY)
26+
@Column(name = "id")
27+
private Long id;
28+
29+
@Column(name = "phone_number", nullable = false, unique = true, length = 20)
30+
private String phoneNumber;
31+
32+
@Column(name = "nickname", nullable = false, length = 20)
33+
private String nickname;
34+
35+
@Column(name = "score", nullable = false)
36+
private int score;
37+
38+
@Column(name = "stage_reached", nullable = false)
39+
private int stageReached;
40+
41+
/**
42+
* ์ƒˆ ์ ์ˆ˜๊ฐ€ ํ˜„์žฌ ์ ์ˆ˜๋ณด๋‹ค ๋†’์„ ๊ฒฝ์šฐ ์—”ํ‹ฐํ‹ฐ์˜ ๋‹‰๋„ค์ž„, ์ ์ˆ˜ ๋ฐ ๋„๋‹ฌ ์Šคํ…Œ์ด์ง€๋ฅผ ๊ฐฑ์‹ ํ•œ๋‹ค.
43+
*
44+
* @param nickname ๊ฐฑ์‹ ํ•  ๋‹‰๋„ค์ž„
45+
* @param newScore ๋น„๊ตํ•  ์ƒˆ ์ ์ˆ˜(ํ˜„์žฌ ์ ์ˆ˜๋ณด๋‹ค ํด ๋•Œ ์ ์šฉ๋จ)
46+
* @param newStageReached ๊ฐฑ์‹ ํ•  ๋„๋‹ฌํ•œ ์Šคํ…Œ์ด์ง€
47+
*/
48+
public void updateIfHigherScore(String nickname, int newScore, int newStageReached) {
49+
if (this.score < newScore) {
50+
this.nickname = nickname;
51+
this.score = newScore;
52+
this.stageReached = newStageReached;
53+
}
54+
}
55+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package inha.gdgoc.domain.game.repository;
2+
3+
import inha.gdgoc.domain.game.entity.Rythm8beatScore;
4+
import java.util.List;
5+
import java.util.Optional;
6+
import org.springframework.data.jpa.repository.JpaRepository;
7+
import org.springframework.data.jpa.repository.Query;
8+
import org.springframework.data.repository.query.Param;
9+
10+
public interface Rythm8beatScoreRepository extends JpaRepository<Rythm8beatScore, Long> {
11+
12+
/**
13+
* ์ฃผ์–ด์ง„ ํœด๋Œ€์ „ํ™” ๋ฒˆํ˜ธ๋กœ ์ €์žฅ๋œ ์ ์ˆ˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
14+
*
15+
* @param phoneNumber ์กฐํšŒํ•  ํœด๋Œ€์ „ํ™” ๋ฒˆํ˜ธ
16+
* @return `Optional`์— ์กฐํšŒ๋œ Rythm8beatScore๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ, ์—†์œผ๋ฉด ๋น„์–ด ์žˆ์Œ
17+
*/
18+
Optional<Rythm8beatScore> findByPhoneNumber(String phoneNumber);
19+
20+
/**
21+
* ์ƒ์œ„ 3๊ฐœ์˜ Rythm8beatScore ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ ์ˆ˜ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ, ๋™์ ์ผ ๊ฒฝ์šฐ ์—…๋ฐ์ดํŠธ ์‹œ๊ฐ„ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
22+
*
23+
* @return ์ •๋ ฌ๋œ ์ตœ๋Œ€ 3๊ฐœ์˜ Rythm8beatScore ๋ชฉ๋ก. ์ผ์น˜ํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์—†์œผ๋ฉด ๋นˆ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
24+
*/
25+
List<Rythm8beatScore> findTop3ByOrderByScoreDescUpdatedAtAsc();
26+
27+
/**
28+
* ์ง€์ •ํ•œ ์ ์ˆ˜๋ณด๋‹ค ํฐ Rythm8beatScore ์—”ํ‹ฐํ‹ฐ์˜ ๊ฐœ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
29+
*
30+
* @param score ๋น„๊ต ๊ธฐ์ค€์ด ๋˜๋Š” ์ ์ˆ˜(์ด ๊ฐ’์„ ์ดˆ๊ณผํ•˜๋Š” ํ•ญ๋ชฉ์„ ์…€ ์ž„๊ณ„๊ฐ’)
31+
* @return ์ง€์ •ํ•œ `score`๋ณด๋‹ค ํฐ ์ ์ˆ˜๋ฅผ ๊ฐ€์ง„ ์—”ํ‹ฐํ‹ฐ์˜ ์ˆ˜
32+
*/
33+
@Query("SELECT COUNT(r) FROM Rythm8beatScore r WHERE r.score > :score")
34+
long countByScoreGreaterThan(@Param("score") int score);
35+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package inha.gdgoc.domain.game.service;
2+
3+
import inha.gdgoc.domain.game.dto.request.Rythm8beatScoreRequest;
4+
import inha.gdgoc.domain.game.dto.response.Rythm8beatRankItemResponse;
5+
import inha.gdgoc.domain.game.dto.response.Rythm8beatRankingResponse;
6+
import inha.gdgoc.domain.game.entity.Rythm8beatScore;
7+
import inha.gdgoc.domain.game.repository.Rythm8beatScoreRepository;
8+
import java.util.List;
9+
import java.util.stream.IntStream;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.stereotype.Service;
12+
import org.springframework.transaction.annotation.Transactional;
13+
14+
@Service
15+
@RequiredArgsConstructor
16+
@Transactional
17+
public class Rythm8beatScoreService {
18+
19+
private final Rythm8beatScoreRepository rythm8beatScoreRepository;
20+
21+
/**
22+
* ํ”Œ๋ ˆ์ด์–ด์˜ ์ ์ˆ˜๋ฅผ ์ œ์ถœํ•˜๊ณ , ๋™์ผ ์ „ํ™”๋ฒˆํ˜ธ์˜ ๊ธฐ์กด ๊ธฐ๋ก์ด ์žˆ์œผ๋ฉด ๋” ๋†’์€ ์ ์ˆ˜๋กœ๋งŒ ๊ฐฑ์‹ ํ•˜๋ฉฐ ์—†์œผ๋ฉด ์ƒˆ ๊ธฐ๋ก์„ ์ €์žฅํ•œ๋‹ค.
23+
*
24+
* @param request ์ œ์ถœํ•  ์ ์ˆ˜ ์ •๋ณด. phoneNumber, nickname, score ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ stageReached๊ฐ€ null์ด๋ฉด 1๋กœ ๊ฐ„์ฃผํ•œ๋‹ค.
25+
*/
26+
public void submitScore(Rythm8beatScoreRequest request) {
27+
rythm8beatScoreRepository.findByPhoneNumber(request.getPhoneNumber())
28+
.ifPresentOrElse(
29+
entity -> entity.updateIfHigherScore(
30+
request.getNickname(),
31+
request.getScore(),
32+
request.getStageReached() != null ? request.getStageReached() : 1
33+
),
34+
() -> rythm8beatScoreRepository.save(Rythm8beatScore.builder()
35+
.phoneNumber(request.getPhoneNumber())
36+
.nickname(request.getNickname())
37+
.score(request.getScore())
38+
.stageReached(request.getStageReached() != null ? request.getStageReached() : 1)
39+
.build())
40+
);
41+
}
42+
43+
/**
44+
* ์ƒ์œ„ 3๋ช…์˜ ์ ์ˆ˜์™€ ์š”์ฒญ๋œ ์‚ฌ์šฉ์ž์˜ ๋žญํ‚น ์ •๋ณด๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
45+
*
46+
* @param phoneNumber ์กฐํšŒํ•  ์‚ฌ์šฉ์ž์˜ ์ „ํ™”๋ฒˆํ˜ธ. null ๋˜๋Š” ๊ณต๋ฐฑ์ด๋ฉด ์‚ฌ์šฉ์ž ๋žญํ‚น ํ•ญ๋ชฉ์€ ํฌํ•จ๋˜์ง€ ์•Š๋Š”๋‹ค.
47+
* @return ์ƒ์œ„ 3๋ช… ํ•ญ๋ชฉ ๋ฆฌ์ŠคํŠธ์™€ ์š”์ฒญ ์‚ฌ์šฉ์ž์˜ ๋žญํ‚น ํ•ญ๋ชฉ(์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด null)์„ ํฌํ•จํ•œ ๋žญํ‚น ์‘๋‹ต ๊ฐ์ฒด.
48+
*/
49+
@Transactional(readOnly = true)
50+
public Rythm8beatRankingResponse getRanking(String phoneNumber) {
51+
List<Rythm8beatScore> top3 = rythm8beatScoreRepository.findTop3ByOrderByScoreDescUpdatedAtAsc();
52+
53+
List<Rythm8beatRankItemResponse> top3Response = IntStream.range(0, top3.size())
54+
.mapToObj(i -> new Rythm8beatRankItemResponse(i + 1, top3.get(i).getNickname(), top3.get(i).getScore()))
55+
.toList();
56+
57+
Rythm8beatRankItemResponse userRank = null;
58+
if (phoneNumber != null && !phoneNumber.isBlank()) {
59+
userRank = rythm8beatScoreRepository.findByPhoneNumber(phoneNumber)
60+
.map(gs -> {
61+
long rank = rythm8beatScoreRepository.countByScoreGreaterThan(gs.getScore()) + 1;
62+
return new Rythm8beatRankItemResponse((int) rank, gs.getNickname(), gs.getScore());
63+
})
64+
.orElse(null);
65+
}
66+
67+
return new Rythm8beatRankingResponse(top3Response, userRank);
68+
}
69+
70+
/**
71+
* ์ €์žฅ๋œ ๋ชจ๋“  ๋ฆฌ๋“ฌ8๋น„ํŠธ ์ ์ˆ˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.
72+
*/
73+
public void resetAll() {
74+
rythm8beatScoreRepository.deleteAll();
75+
}
76+
}

โ€Žsrc/main/java/inha/gdgoc/global/security/SecurityConfig.javaโ€Ž

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
9090
return http.build();
9191
}
9292

93+
/**
94+
* ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด ๊ฒฝ๋กœ์— ๋Œ€ํ•œ CORS ์„ค์ •์„ ์ƒ์„ฑํ•˜์—ฌ ๋“ฑ๋กํ•œ๋‹ค.
95+
*
96+
* ์ƒ์„ฑ๋œ CORS ๊ตฌ์„ฑ์€ ์ง€์ •๋œ ์ถœ์ฒ˜ ๋ชฉ๋ก, ํ—ˆ์šฉ HTTP ๋ฉ”์„œ๋“œ, ํ—ˆ์šฉ/๋…ธ์ถœ ํ—ค๋”, ์ž๊ฒฉ์ฆ๋ช… ํ—ˆ์šฉ ๋ฐ ํ”„๋ฆฌํ”Œ๋ผ์ดํŠธ ์บ์‹œ(maxAge)๋ฅผ ํฌํ•จํ•œ๋‹ค.
97+
*
98+
* @return ๋ชจ๋“  ๊ฒฝ๋กœ("/**")์— ๋“ฑ๋ก๋œ CorsConfiguration์„ ์ œ๊ณตํ•˜๋Š” CorsConfigurationSource ๊ฐ์ฒด
99+
*/
93100
@Bean
94101
public CorsConfigurationSource corsConfigurationSource() {
95102
CorsConfiguration config = new CorsConfiguration();
@@ -100,7 +107,8 @@ public CorsConfigurationSource corsConfigurationSource() {
100107
"https://www.gdgocinha.com",
101108
"https://typing-game-alpha-umber.vercel.app",
102109
"https://api.gdgocinha.com",
103-
"https://*.gdgocinha.com"
110+
"https://*.gdgocinha.com",
111+
"https://smpringles24.github.io"
104112
));
105113
config.setAllowedMethods(List.of("GET","POST","PUT","DELETE","OPTIONS","PATCH"));
106114
config.setAllowedHeaders(List.of("Origin","X-Requested-With","Content-Type","Accept","Authorization"));

0 commit comments

Comments
ย (0)