-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(history): 스낵게임 전적을 조회할 수 있다 (#160)
- Loading branch information
Showing
8 changed files
with
270 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 0 additions & 42 deletions
42
src/main/java/com/snackgame/server/history/controller/GameHistoryController.java
This file was deleted.
Oops, something went wrong.
45 changes: 45 additions & 0 deletions
45
src/main/java/com/snackgame/server/history/controller/GameHistoryController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.snackgame.server.history.controller | ||
|
||
import com.snackgame.server.auth.token.support.Authenticated | ||
import com.snackgame.server.history.controller.dto.GameHistoryResponse | ||
import com.snackgame.server.history.dao.SnackgameHistoryDao | ||
import com.snackgame.server.member.domain.Member | ||
import io.swagger.v3.oas.annotations.Operation | ||
import io.swagger.v3.oas.annotations.tags.Tag | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.bind.annotation.RequestParam | ||
import org.springframework.web.bind.annotation.RestController | ||
|
||
@Tag(name = "📈 전적") | ||
@RestController | ||
class GameHistoryController( | ||
private val snackgameHistoryDao: SnackgameHistoryDao | ||
) { | ||
|
||
@GetMapping("/histories/me") | ||
@Operation( | ||
summary = "자신의 게임 전적 조회", | ||
description = """ | ||
`DATE`: 최근 7일의 최고 점수 추이를 조회한다. | ||
`SESSION`: 최근 25게임의 점수들을 조회한다.""" | ||
) | ||
fun showScoresBySessions( | ||
@Authenticated member: Member, | ||
@RequestParam("by") criteria: Criteria | ||
): List<GameHistoryResponse> { | ||
if (criteria == Criteria.DATE) { | ||
return snackgameHistoryDao.selectByDate(member.id) | ||
} | ||
return snackgameHistoryDao.selectBySession(member.id, GAME_RECORD_SIZE) | ||
} | ||
|
||
enum class Criteria { | ||
DATE, | ||
SESSION | ||
} | ||
|
||
companion object { | ||
private const val GAME_RECORD_SIZE = 25 | ||
} | ||
} |
19 changes: 0 additions & 19 deletions
19
src/main/java/com/snackgame/server/history/controller/dto/GameHistoryResponse.java
This file was deleted.
Oops, something went wrong.
10 changes: 10 additions & 0 deletions
10
src/main/java/com/snackgame/server/history/controller/dto/GameHistoryResponse.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.snackgame.server.history.controller.dto | ||
|
||
import java.time.LocalDate | ||
|
||
data class GameHistoryResponse( | ||
val sessionId: Long, | ||
val memberId: Long, | ||
val score: Int, | ||
val updatedAt: LocalDate | ||
) |
58 changes: 58 additions & 0 deletions
58
src/main/java/com/snackgame/server/history/dao/SnackgameHistoryDao.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.snackgame.server.history.dao | ||
|
||
import com.snackgame.server.history.controller.dto.GameHistoryResponse | ||
import org.springframework.jdbc.core.JdbcTemplate | ||
import org.springframework.jdbc.core.RowMapper | ||
import org.springframework.stereotype.Component | ||
import java.sql.ResultSet | ||
import java.time.LocalDate | ||
|
||
@Component | ||
class SnackgameHistoryDao( | ||
private val jdbcTemplate: JdbcTemplate | ||
) { | ||
|
||
fun selectByDate( | ||
memberId: Long, | ||
baseDate: LocalDate = LocalDate.now().minusDays(7 - 1) | ||
): List<GameHistoryResponse> { | ||
val sql = | ||
"""SELECT MAX(session_id) as session_id, owner_id, MAX(score) as score, DATE(expires_at) as expires_at | ||
FROM snackgame | ||
WHERE owner_id = ? AND ? <= date(expires_at) AND date(expires_at) <= now() | ||
GROUP BY DATE(expires_at) ORDER BY expires_at DESC""" | ||
|
||
return jdbcTemplate.query( | ||
sql, | ||
GAME_HISTORY_DTO_ROW_MAPPER, | ||
memberId, | ||
baseDate | ||
) | ||
} | ||
|
||
fun selectBySession(memberId: Long, size: Int): List<GameHistoryResponse> { | ||
val sql = """SELECT session_id, owner_id, score, expires_at | ||
FROM snackgame | ||
WHERE owner_id = ? AND date(expires_at) <= now() | ||
ORDER BY expires_at desc | ||
limit ?""" | ||
|
||
return jdbcTemplate.query( | ||
sql, | ||
GAME_HISTORY_DTO_ROW_MAPPER, | ||
memberId, | ||
size | ||
) | ||
} | ||
|
||
companion object { | ||
private val GAME_HISTORY_DTO_ROW_MAPPER = (RowMapper { rs: ResultSet, _: Int -> | ||
GameHistoryResponse( | ||
rs.getLong("session_id"), | ||
rs.getLong("owner_id"), | ||
rs.getInt("score"), | ||
rs.getDate("expires_at").toLocalDate() | ||
) | ||
}) | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
src/test/java/com/snackgame/server/history/dao/SnackgameHistoryDaoTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
@file:Suppress("NonAsciiCharacters") | ||
|
||
package com.snackgame.server.history.dao | ||
|
||
import com.snackgame.server.history.fixture.SnackgameFixture | ||
import com.snackgame.server.member.fixture.MemberFixture | ||
import com.snackgame.server.member.fixture.MemberFixture.땡칠 | ||
import com.snackgame.server.support.fixture.FixtureSaver | ||
import com.snackgame.server.support.general.DatabaseCleaningDataJpaTest | ||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.DisplayNameGeneration | ||
import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores | ||
import org.junit.jupiter.api.Test | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.jdbc.core.JdbcTemplate | ||
import java.time.LocalDate | ||
|
||
@DisplayNameGeneration(ReplaceUnderscores::class) | ||
@DatabaseCleaningDataJpaTest | ||
class SnackgameHistoryDaoTest { | ||
|
||
@Autowired | ||
private lateinit var jdbcTemplate: JdbcTemplate | ||
|
||
private lateinit var snackgameHistoryDao: SnackgameHistoryDao | ||
|
||
|
||
@BeforeEach | ||
fun setUp() { | ||
MemberFixture.saveAll() | ||
SnackgameFixture.saveAll() | ||
this.snackgameHistoryDao = SnackgameHistoryDao(jdbcTemplate) | ||
} | ||
|
||
@Test | ||
fun `최근 25게임의 전적을 조회할 수 있다`() { | ||
repeat(25) { FixtureSaver.save(SnackgameFixture.eighth()) } | ||
|
||
val scores = snackgameHistoryDao.selectBySession(땡칠().id, 25) | ||
.map { it.score } | ||
|
||
assertThat(scores).containsOnly(800) | ||
} | ||
|
||
@Test | ||
fun `전적을 기간으로 조회할 때는 각 날짜 별 최고점수를 기준으로 한다`() { | ||
val baseDate = LocalDate.now().minusDays(3).plusDays(1) | ||
|
||
assertThat(snackgameHistoryDao.selectByDate(땡칠().id, baseDate)).hasSize(3) | ||
} | ||
|
||
@Test | ||
fun `전적을 기간으로 조회할 때 최고점수보다 낮은 점수는 고려되지 않는다`() { | ||
val scores = snackgameHistoryDao.selectByDate(땡칠().id) | ||
.map { it.score } | ||
|
||
assertThat(scores).doesNotContain(SnackgameFixture.second().score, SnackgameFixture.sixth().score) | ||
} | ||
|
||
@Test | ||
fun `전적을 기간으로 조회할 때는 최신날짜부터 조회한다`() { | ||
val scores = snackgameHistoryDao.selectByDate(땡칠().id) | ||
.map { it.score } | ||
|
||
assertThat(scores).doesNotContain(SnackgameFixture.first().score) | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
src/test/java/com/snackgame/server/history/fixture/SnackgameFixture.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package com.snackgame.server.history.fixture | ||
|
||
import com.snackgame.server.game.snackgame.domain.Snackgame | ||
import com.snackgame.server.member.fixture.MemberFixture.땡칠 | ||
import com.snackgame.server.support.fixture.FixtureSaver | ||
import java.time.Duration | ||
|
||
object SnackgameFixture { | ||
|
||
fun first() = Snackgame( | ||
땡칠().id, | ||
Duration.ofDays(7).negated(), | ||
100 | ||
) | ||
|
||
fun second() = Snackgame( | ||
땡칠().id, | ||
Duration.ofDays(6).negated(), | ||
200 | ||
) | ||
|
||
fun secondBest() = Snackgame( | ||
땡칠().id, | ||
Duration.ofDays(6).negated(), | ||
250 | ||
) | ||
|
||
fun third() = Snackgame( | ||
땡칠().id, | ||
Duration.ofDays(5).negated(), | ||
300 | ||
) | ||
|
||
fun fourth() = Snackgame( | ||
땡칠().id, | ||
Duration.ofDays(4).negated(), | ||
400 | ||
) | ||
|
||
fun fifth() = Snackgame( | ||
땡칠().id, | ||
Duration.ofDays(3).negated(), | ||
500 | ||
) | ||
|
||
fun sixth() = Snackgame( | ||
땡칠().id, | ||
Duration.ofDays(2).negated(), | ||
600 | ||
) | ||
|
||
fun sixthBest() = Snackgame( | ||
땡칠().id, | ||
Duration.ofDays(2).negated(), | ||
650 | ||
) | ||
|
||
fun seventh() = Snackgame( | ||
땡칠().id, | ||
Duration.ofDays(1).negated(), | ||
700 | ||
) | ||
|
||
fun eighth() = Snackgame( | ||
땡칠().id, | ||
Duration.ZERO, | ||
800 | ||
) | ||
|
||
fun saveAll() { | ||
FixtureSaver.save( | ||
first(), | ||
second(), | ||
secondBest(), | ||
third(), | ||
fourth(), | ||
fifth(), | ||
sixth(), | ||
sixthBest(), | ||
seventh(), | ||
eighth() | ||
) | ||
} | ||
} |