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

✨ :: RoutineHistory 생성 API #152

Merged
merged 8 commits into from
Mar 11, 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 @@ -4,21 +4,26 @@ object TableNames {

private const val TABLE_PREFIX = "tbl_"
private const val INDEX_PREFIX = "idx_"
private const val UNIQUE_PREFIX = "unique_"
private const val UNIQUE_PREFIX = "unique_idx_"

const val USER_TABLE = "${TABLE_PREFIX}user"

const val POSE_TABLE = "${TABLE_PREFIX}pose"

const val ROUTINE_TABLE = "${TABLE_PREFIX}routine"
const val ROUTINE_HISTORY_TABLE = "${TABLE_PREFIX}routine_history"

const val ROUTINE_HISTORY_DATE_USER_UNIQUE = UNIQUE_PREFIX + "date_user"

const val PICKLE_TABLE = "${TABLE_PREFIX}pickle"
const val PICKLE_MAP_TABLE = "${TABLE_PREFIX}pickle_comments"
const val PICKLE_COMMENT_TABLE = "${TABLE_PREFIX}pickle_comment"
const val PICKLE_REPLY_TABLE = "${TABLE_PREFIX}pickle_reply"
const val PICKLE_TAG_INDEX = "${INDEX_PREFIX}pickle_tag"

const val PICKLE_TAG_INDEX = "${INDEX_PREFIX}tag"

const val PICKLE_LIKE_TABLE = "${TABLE_PREFIX}pickle_like"
const val PICKLE_LIKE_INDEX = "${INDEX_PREFIX}pickle_like"
const val PICKLE_LIKE_INDEX = "${INDEX_PREFIX}like"

const val REPORT_TABLE = "${TABLE_PREFIX}report"

Expand All @@ -30,10 +35,10 @@ object TableNames {
const val REDIS_STEP_TABLE = "${TABLE_PREFIX}step"

const val PURPOSE_TABLE = "${TABLE_PREFIX}purpose"
const val PURPOSE_START_DATE_INDEX = "${INDEX_PREFIX}purpose_start"
const val PURPOSE_END_DATE_INDEX = "${INDEX_PREFIX}purpose_end"
const val PURPOSE_START_DATE_INDEX = "${INDEX_PREFIX}start"
const val PURPOSE_END_DATE_INDEX = "${INDEX_PREFIX}end"

const val DAILY = TABLE_PREFIX + "daily_exercise_complete"
const val DAILY = TABLE_PREFIX + "daily"
const val DAILY_DATE_INDEX = INDEX_PREFIX + "date"
const val DAILY_DATE_UPLOADER_UNIQUE = UNIQUE_PREFIX + "date_uploader"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.info.maeumgagym.domain.routine

import com.info.common.PersistenceAdapter
import com.info.maeumgagym.domain.routine.mapper.RoutineHistoryMapper
import com.info.maeumgagym.domain.routine.repository.RoutineHistoryRepository
import com.info.maeumgagym.routine.model.RoutineHistory
import com.info.maeumgagym.routine.port.out.*
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate
import java.util.*

@PersistenceAdapter
internal class RoutineHistoryPersistenceAdapter(
private val mapper: RoutineHistoryMapper,
private val routineHistoryRepository: RoutineHistoryRepository
) : SaveRoutineHistoryPort, ReadRoutineHistoryPort, ExistsRoutineHistoryPort {

@Transactional(propagation = Propagation.MANDATORY)
override fun save(routineHistory: RoutineHistory): RoutineHistory =
routineHistory.run {
mapper.toDomain(routineHistoryRepository.save(mapper.toEntity(this)))
}

override fun readByUserIdAndDate(userId: UUID, date: LocalDate): RoutineHistory? =
routineHistoryRepository.findByUserIdAndDate(userId, date)?.run {
mapper.toDomain(this)
}

override fun exsitsByUserIdAndDate(userId: UUID, date: LocalDate): Boolean =
routineHistoryRepository.findByUserIdAndDate(userId, date) != null
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ internal class RoutinePersistenceAdapter(
routineMapper.toDomain(this)
}

override fun readByUserIdAndDayOfWeek(userId: UUID, dayOfWeek: DayOfWeek): Routine? =
routineNativeRepository.findByUserIdAndDayOfWeek(userId, dayOfWeek.name)?.run {
routineMapper.toDomain(this)
}

@Transactional(propagation = Propagation.MANDATORY)
override fun delete(routine: Routine) =
routineRepository.delete(routineMapper.toEntity(routine))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.info.maeumgagym.domain.routine.entity

import com.info.maeumgagym.TableNames
import com.info.maeumgagym.domain.base.BaseLongIdEntity
import java.time.LocalDate
import java.util.*
import javax.persistence.*

@Entity(name = TableNames.ROUTINE_HISTORY_TABLE)
@Table(
uniqueConstraints = [
UniqueConstraint(
name = TableNames.ROUTINE_HISTORY_DATE_USER_UNIQUE,
columnNames = ["user_id", "date"]
)
]
)
class RoutineHistoryJpaEntity(
id: Long?,
userId: UUID,
date: LocalDate,
exerciseInfoList: MutableList<ExerciseInfo>
) : BaseLongIdEntity(id) {

@ElementCollection(fetch = FetchType.LAZY)
var exerciseInfoList: MutableList<ExerciseInfo> = exerciseInfoList
protected set

@Column(name = "user_id", updatable = false, columnDefinition = "BINARY(16)", nullable = false)
var userId: UUID = userId
protected set

@Column(name = "date", updatable = false, nullable = false)
var date: LocalDate = date
protected set
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.info.maeumgagym.domain.routine.mapper

import com.info.maeumgagym.domain.routine.entity.ExerciseInfo
import com.info.maeumgagym.domain.routine.entity.RoutineHistoryJpaEntity
import com.info.maeumgagym.routine.model.ExerciseInfoModel
import com.info.maeumgagym.routine.model.RoutineHistory
import org.springframework.stereotype.Component

@Component
class RoutineHistoryMapper {
fun toEntity(model: RoutineHistory): RoutineHistoryJpaEntity = model.run {
RoutineHistoryJpaEntity(
id = id,
exerciseInfoList = toExerciseInfoList(exerciseInfoList),
userId = userId,
date = date
)
}

fun toDomain(entity: RoutineHistoryJpaEntity): RoutineHistory = entity.run {
RoutineHistory(
id = id,
exerciseInfoList = toExerciseInfoModelList(exerciseInfoList),
userId = userId,
date = date
)
}

private fun toExerciseInfoList(exerciseInfoList: MutableList<ExerciseInfoModel>) =
exerciseInfoList.map {
ExerciseInfo(
exerciseName = it.exerciseName,
repetitions = it.repetitions,
sets = it.sets
)
}.toMutableList()

private fun toExerciseInfoModelList(exerciseInfoList: MutableList<ExerciseInfo>) =
exerciseInfoList.map {
ExerciseInfoModel(
exerciseName = it.exerciseName,
repetitions = it.repetitions,
sets = it.sets
)
}.toMutableList()
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ import com.info.maeumgagym.routine.model.ExerciseInfoModel
import com.info.maeumgagym.routine.model.Routine
import com.info.maeumgagym.routine.model.RoutineStatusModel
import org.springframework.stereotype.Component
import javax.persistence.EntityManager

@Component
class RoutineMapper(
private val em: EntityManager
) {
class RoutineMapper {
fun toEntity(routine: Routine): RoutineJpaEntity = routine.run {
RoutineJpaEntity(
id = id,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.info.maeumgagym.domain.routine.repository

import com.info.maeumgagym.domain.routine.entity.RoutineHistoryJpaEntity
import org.springframework.data.repository.Repository
import java.time.LocalDate
import java.util.*

@org.springframework.stereotype.Repository
interface RoutineHistoryRepository : Repository<RoutineHistoryJpaEntity, Long> {

fun save(entity: RoutineHistoryJpaEntity): RoutineHistoryJpaEntity

fun findByUserIdAndDate(userId: UUID, date: LocalDate): RoutineHistoryJpaEntity?
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,16 @@ interface RoutineNativeRepository : Repository<RoutineJpaEntity, Long?> {
@Param("userId") userId: UUID,
@Param("dayOfWeek") dayOfWeek: String
): RoutineJpaEntity?

@Query(
value = "SELECT * FROM ${TableNames.ROUTINE_TABLE} r " +
"INNER JOIN ${TableNames.ROUTINE_TABLE}_day_of_weeks d " +
"ON r.id = d.${TableNames.ROUTINE_TABLE}_id " +
"WHERE r.user_id = :userId AND d.day_of_weeks LIKE :dayOfWeek",
nativeQuery = true
)
fun findByUserIdAndDayOfWeek(
@Param("userId") userId: UUID,
@Param("dayOfWeek") dayOfWeek: String
): RoutineJpaEntity?
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ open class MaeumGaGymException(
companion object {

// Default
val NO_CONTENT = MaeumGaGymException(204, "There's No Content Left")
val BAD_REQUEST = MaeumGaGymException(400, "Bad Request")
val UNAUTHORIZED = MaeumGaGymException(401, "Unauthorized")
val FORBIDDEN = MaeumGaGymException(403, "Forbidden")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ data class RoutineResponse(
val routineName: String,
val exerciseInfoList: List<ExerciseInfoDto>,
val dayOfWeeks: List<String>?,
val routineStatus: RoutineStatusDto
val routineStatus: RoutineStatusDto,
val isCompleted: Boolean? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,30 @@ data class Routine(
)
}
)

fun toResponse(isCompleted: Boolean): RoutineResponse =
RoutineResponse(
id = id!!,
routineName = routineName,
exerciseInfoList = exerciseInfoModelList.map {
ExerciseInfoDto(
exerciseName = it.exerciseName,
repetitions = it.repetitions,
sets = it.sets
)
},
dayOfWeeks = dayOfWeeks?.map {
it.getDisplayName(
TextStyle.FULL,
Locale.KOREA
)
},
routineStatus = routineStatusModel.run {
RoutineStatusDto(
isArchived = isArchived,
isShared = isShared
)
},
isCompleted = isCompleted
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.info.maeumgagym.routine.model

import java.time.LocalDate
import java.util.*

data class RoutineHistory(
val id: Long?,
val userId: UUID,
val date: LocalDate,
val exerciseInfoList: MutableList<ExerciseInfoModel>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.info.maeumgagym.routine.port.`in`

interface CompleteTodayRoutineUseCase {
fun complete()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.info.maeumgagym.routine.port.out

import java.time.LocalDate
import java.util.*

interface ExistsRoutineHistoryPort {

fun exsitsByUserIdAndDate(userId: UUID, date: LocalDate): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.info.maeumgagym.routine.port.out

import com.info.maeumgagym.routine.model.RoutineHistory
import java.time.LocalDate
import java.util.*

interface ReadRoutineHistoryPort {

fun readByUserIdAndDate(userId: UUID, date: LocalDate): RoutineHistory?
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ interface ReadRoutinePort {
fun readAllByUserId(userId: UUID): List<Routine>

fun readByUserIdAndDayOfWeekAndIsArchivedFalse(userId: UUID, dayOfWeek: DayOfWeek): Routine?

fun readByUserIdAndDayOfWeek(userId: UUID, dayOfWeek: DayOfWeek): Routine?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.info.maeumgagym.routine.port.out

import com.info.maeumgagym.routine.model.RoutineHistory

interface SaveRoutineHistoryPort {

fun save(routineHistory: RoutineHistory): RoutineHistory
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.info.maeumgagym.routine.service

import com.info.common.UseCase
import com.info.maeumgagym.auth.port.out.ReadCurrentUserPort
import com.info.maeumgagym.common.exception.BusinessLogicException
import com.info.maeumgagym.routine.model.RoutineHistory
import com.info.maeumgagym.routine.port.`in`.CompleteTodayRoutineUseCase
import com.info.maeumgagym.routine.port.out.ExistsRoutineHistoryPort
import com.info.maeumgagym.routine.port.out.ReadRoutinePort
import com.info.maeumgagym.routine.port.out.SaveRoutineHistoryPort
import java.time.LocalDate

@UseCase
class CompleteTodayRoutineService(
private val readCurrentUserPort: ReadCurrentUserPort,
private val readRoutinePort: ReadRoutinePort,
private val saveRoutineHistoryPort: SaveRoutineHistoryPort,
private val existsRoutineHistoryPort: ExistsRoutineHistoryPort
) : CompleteTodayRoutineUseCase {
override fun complete() {
val user = readCurrentUserPort.readCurrentUser()

val now = LocalDate.now()

val routine = readRoutinePort.readByUserIdAndDayOfWeek(user.id!!, now.dayOfWeek)
?: throw BusinessLogicException.ROUTINE_NOT_FOUND

if (existsRoutineHistoryPort.exsitsByUserIdAndDate(user.id, now)) {
throw BusinessLogicException(409, "Already Completed Routine")
} else {
saveRoutineHistoryPort.save(
RoutineHistory(
id = null,
date = now,
exerciseInfoList = routine.exerciseInfoModelList,
userId = user.id
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,30 @@ package com.info.maeumgagym.routine.service

import com.info.common.ReadOnlyUseCase
import com.info.maeumgagym.auth.port.out.ReadCurrentUserPort
import com.info.maeumgagym.common.exception.MaeumGaGymException
import com.info.maeumgagym.routine.dto.response.RoutineResponse
import com.info.maeumgagym.routine.port.`in`.ReadTodayRoutineUseCase
import com.info.maeumgagym.routine.port.out.ExistsRoutineHistoryPort
import com.info.maeumgagym.routine.port.out.ReadRoutinePort
import java.time.LocalDate

@ReadOnlyUseCase
class ReadTodayRoutineService(
private val readRoutinePort: ReadRoutinePort,
private val readCurrentUserPort: ReadCurrentUserPort
private val readCurrentUserPort: ReadCurrentUserPort,
private val existsRoutineHistoryPort: ExistsRoutineHistoryPort
) : ReadTodayRoutineUseCase {

override fun readTodayRoutine(): RoutineResponse? =
readRoutinePort.readByUserIdAndDayOfWeekAndIsArchivedFalse(
readCurrentUserPort.readCurrentUser().id!!,
LocalDate.now().dayOfWeek
)?.run { toResponse() }
override fun readTodayRoutine(): RoutineResponse? {
val userId = readCurrentUserPort.readCurrentUser().id!!

val now = LocalDate.now()

return readRoutinePort.readByUserIdAndDayOfWeekAndIsArchivedFalse(
userId,
now.dayOfWeek
)?.run {
toResponse(existsRoutineHistoryPort.exsitsByUserIdAndDate(userId, now))
} ?: throw MaeumGaGymException.NO_CONTENT
}
}
Loading
Loading