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(history): 스낵게임 전적을 조회할 수 있다 #160

Merged
merged 1 commit into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
)
}
}
Loading