Skip to content
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 @@ -4,7 +4,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties(prefix = "app")
data class AppProperties(
val token: TokenProperties
val token: TokenProperties,
val depromeet: DepromeetProperties,
) {
data class TokenProperties(
val secretKey: String,
Expand All @@ -15,5 +16,9 @@ data class AppProperties(
val refresh: Long,
)
}

data class DepromeetProperties(
val generation: Int,
)
}

70 changes: 70 additions & 0 deletions src/main/kotlin/com/depromeet/makers/domain/Session.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.depromeet.makers.domain

import com.depromeet.makers.domain.enums.SessionType
import com.depromeet.makers.domain.vo.Code
import com.depromeet.makers.domain.vo.SessionPlace
import org.bson.types.ObjectId
import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.index.CompoundIndex
import org.springframework.data.mongodb.core.mapping.Document
import java.time.LocalDateTime

@Document("session")
@CompoundIndex(def = "{'generation': 1, 'week': 1}", unique = true)
class Session(
@Id
val id: ObjectId,
var generation: Int,
var week: Int,
var title: String,
var description: String,
var place: SessionPlace?,
var code: Code = Code.generate(),
var startTime: LocalDateTime,
var endTime: LocalDateTime,
) {
fun update(
generation: Int = this.generation,
week: Int = this.week,
title: String = this.title,
description: String = this.description,
place: SessionPlace? = this.place,
startTime: LocalDateTime = this.startTime,
endTime: LocalDateTime = this.endTime,
) {
this.generation = generation
this.week = week
this.title = title
this.description = description
this.place = place
this.startTime = startTime
this.endTime = endTime
}

fun getType(): SessionType {
return SessionType.of(this)
}

companion object {
fun create(
generation: Int,
week: Int,
title: String,
description: String,
place: SessionPlace?,
startTime: LocalDateTime,
endTime: LocalDateTime,
): Session {
return Session(
id = ObjectId(),
generation = generation,
week = week,
title = title,
description = description,
place = place,
startTime = startTime,
endTime = endTime,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.depromeet.makers.domain.enums

import com.depromeet.makers.domain.Session

enum class SessionType {
ONLINE,
OFFLINE,
;

companion object {
fun of(session: Session): SessionType {
return when (session.place) {
null -> ONLINE
else -> OFFLINE
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ enum class ErrorCode(
val message: String,
) {
INTERNAL_ERROR(5000, "알 수 없는 오류가 발생했어요"),
MEMBER_NOT_FOUND(4000, "테스트를 찾을 수 없어요"),
NOT_FOUND(4000, "존재하지 않아요"),
TOKEN_EXPIRED(4001, "토큰이 만료되었어요"),
TOKEN_NOT_FOUND(4002, "토큰이 없어요"),
TOKEN_NOT_VALID(4003, "토큰이 유효하지 않아요"),
Expand Down
18 changes: 18 additions & 0 deletions src/main/kotlin/com/depromeet/makers/domain/vo/Code.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.depromeet.makers.domain.vo

import kotlin.random.Random

@JvmInline
value class Code(
val code: String,
) {
fun mask(): Code {
return Code("******")
}

companion object {
fun generate(): Code {
return Random.nextInt(100_000, 1_000_000).toString().let { Code(it) }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.depromeet.makers.domain.vo

data class SessionPlace(
val id: String,
val title: String,
val address: String,
val latitude: Double,
val longitude: Double,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.depromeet.makers.presentation.web

import com.depromeet.makers.presentation.web.dto.request.SessionGenerationRequest
import com.depromeet.makers.presentation.web.dto.request.SessionRequest
import com.depromeet.makers.presentation.web.dto.response.SessionResponse
import com.depromeet.makers.service.SessionService
import com.depromeet.makers.util.AuthenticationUtil.isAdmin
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.bson.types.ObjectId
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.Authentication
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@Tag(name = "세션 API", description = "세션 관련 API")
@RestController
class SessionController(
private val sessionService: SessionService,
) {
@Operation(summary = "[어드민] 세션 생성", description = "세션을 생성합니다.")
@PreAuthorize("hasRole('ADMIN')")
@PostMapping("/v1/sessions")
fun createSession(
@RequestBody request: SessionRequest,
): SessionResponse {
val session = sessionService.createSession(
generation = request.generation,
week = request.week,
title = request.title,
description = request.description,
place = request.place,
startTime = request.startTime,
endTime = request.endTime,
)
return SessionResponse.from(session)
}

@Operation(summary = "세션 조회", description = "세션을 조회합니다.")
@GetMapping("/v1/sessions/{sessionId}")
fun getSession(
authentication: Authentication,
@PathVariable sessionId: String,
): SessionResponse {
val needMask = authentication.isAdmin().not()
val session = sessionService.getSession(
sessionId = ObjectId(sessionId),
)
return SessionResponse.from(session, needMask)
}

@Operation(summary = "전체 세션 조회", description = "기수에 해당하는 세션을 조회합니다.")
@GetMapping("/v1/sessions")
fun getAllSessionByGeneration(
authentication: Authentication,
@RequestParam generation: Int,
): List<SessionResponse> {
val needMask = authentication.isAdmin().not()
val sessions = sessionService.getAllSessionByGeneration(generation)

return sessions.map { session -> SessionResponse.from(session, needMask) }
}

@Operation(summary = "[어드민] 세션 수정", description = "특정 세션을 수정합니다.")
@PreAuthorize("hasRole('ADMIN')")
@PutMapping("/v1/sessions/{sessionId}")
fun updateSession(
@PathVariable sessionId: String,
@RequestBody request: SessionRequest,
): SessionResponse {
val session = sessionService.updateSession(
sessionId = ObjectId(sessionId),
title = request.title,
description = request.description,
place = request.place,
startTime = request.startTime,
endTime = request.endTime,
)
return SessionResponse.from(session)
}

@Operation(summary = "[어드민] 세션 코드 갱신", description = "특정 세션의 코드를 갱신합니다.")
@PreAuthorize("hasRole('ADMIN')")
@PostMapping("/v1/sessions/{sessionId}/code")
fun updateSessionCode(
authentication: Authentication,
@PathVariable sessionId: String,
): SessionResponse {
val session = sessionService.updateSessionCode(
sessionId = ObjectId(sessionId),
)
return SessionResponse.from(session)
}

@Operation(summary = "[어드민] 세션 삭제", description = "특정 세션을 삭제합니다.")
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/v1/sessions/{sessionId}")
fun deleteSession(
@PathVariable sessionId: String,
) {
sessionService.deleteSession(ObjectId(sessionId))
}

@Operation(summary = "[어드민] 기수 별 세션 전체 삭제", description = "기수에 해당하는 모든 세션을 삭제합니다.")
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/v1/sessions")
fun deleteAllSessionByGeneration(
@RequestBody request: SessionGenerationRequest,
) {
sessionService.deleteAllByGeneration(request.generation)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.depromeet.makers.presentation.web.dto.request

import com.depromeet.makers.domain.vo.SessionPlace
import java.time.LocalDateTime

data class SessionRequest(
val generation: Int,
val week: Int,
val title: String,
val description: String,
val place: SessionPlace?,
val startTime: LocalDateTime,
val endTime: LocalDateTime,
)

data class SessionGenerationRequest(
val generation: Int,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.depromeet.makers.presentation.web.dto.response

import com.depromeet.makers.domain.Session
import com.depromeet.makers.domain.enums.SessionType
import com.depromeet.makers.domain.vo.Code
import com.depromeet.makers.domain.vo.SessionPlace
import java.time.LocalDateTime

data class SessionResponse(
val sessionId: String,
val generation: Int,
val week: Int,
val title: String,
val description: String,
val type: SessionType,
val place: SessionPlace?,
val code: Code,
val startTime: LocalDateTime,
val endTime: LocalDateTime,
) {
companion object {
fun from(session: Session): SessionResponse {
return SessionResponse(
sessionId = session.id.toHexString(),
generation = session.generation,
week = session.week,
title = session.title,
description = session.description,
type = session.getType(),
place = session.place,
startTime = session.startTime,
code = session.code,
endTime = session.endTime,
)
}

fun from(session: Session, mask: Boolean): SessionResponse {
return when (mask) {
false -> from(session)
true -> from(session).copy(
code = session.code.mask()
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.depromeet.makers.repository

import com.depromeet.makers.domain.Session
import org.bson.types.ObjectId
import org.springframework.data.mongodb.repository.MongoRepository

interface SessionRepository : MongoRepository<Session, ObjectId> {
fun findByGeneration(generation: Int): List<Session>

fun findByGenerationAndWeek(generation: Int, week: Int): Session?

fun deleteAllByGeneration(generation: Int)
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class AuthService(

fun refreshWithRefreshToken(refreshToken: String): TokenPair {
val memberId = jwtTokenProvider.getMemberIdFromRefreshToken(refreshToken)
val member = memberRepository.findByIdOrNull(memberId) ?: throw DomainException(ErrorCode.MEMBER_NOT_FOUND)
val member = memberRepository.findByIdOrNull(memberId) ?: throw DomainException(ErrorCode.NOT_FOUND)
return generateTokenFromMember(member)
}

Expand Down
Loading