Skip to content

Commit

Permalink
feat(history): 스낵게임 전적을 조회할 수 있다 (#160)
Browse files Browse the repository at this point in the history
  • Loading branch information
0chil authored Jul 29, 2024
1 parent 07933fb commit a47b0ac
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import com.snackgame.server.game.session.domain.Session
import java.time.Duration
import javax.persistence.Entity


@Entity
class Snackgame(ownerId: Long) : Session(ownerId, SESSION_TIME + SPARE_TIME) {
class Snackgame(
ownerId: Long,
timeLimit: Duration = SESSION_TIME + SPARE_TIME,
score: Int = 0
) : Session(ownerId, timeLimit, score) {

@Deprecated("스트릭 구현 시 제거 예정")
fun setScoreUnsafely(score: Int) {
Expand Down

This file was deleted.

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
}
}

This file was deleted.

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
)
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()
)
})
}
}
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)
}
}
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()
)
}
}

0 comments on commit a47b0ac

Please sign in to comment.