diff --git a/src/main/kotlin/com/depromeet/makers/domain/gateway/SessionGateway.kt b/src/main/kotlin/com/depromeet/makers/domain/gateway/SessionGateway.kt index b318066..4c25c55 100644 --- a/src/main/kotlin/com/depromeet/makers/domain/gateway/SessionGateway.kt +++ b/src/main/kotlin/com/depromeet/makers/domain/gateway/SessionGateway.kt @@ -14,7 +14,7 @@ interface SessionGateway { fun findAllByGeneration(generation: Int): List - fun findByGenerationAndWeek(generation: Int, week: Int): Session + fun findByGenerationAndWeek(generation: Int, week: Int): Session? fun findByStartTimeBetween(startTime: LocalDateTime, endTime: LocalDateTime): Session? } diff --git a/src/main/kotlin/com/depromeet/makers/domain/model/Attendance.kt b/src/main/kotlin/com/depromeet/makers/domain/model/Attendance.kt index d7a0033..f9bc0ac 100644 --- a/src/main/kotlin/com/depromeet/makers/domain/model/Attendance.kt +++ b/src/main/kotlin/com/depromeet/makers/domain/model/Attendance.kt @@ -11,6 +11,7 @@ data class Attendance( val generation: Int, val week: Int, val member: Member, + val sessionType: SessionType, val attendanceStatus: AttendanceStatus, val attendanceTime: LocalDateTime? ) { @@ -80,12 +81,14 @@ data class Attendance( generation: Int, week: Int, member: Member, + sessionType: SessionType = SessionType.ONLINE, attendance: AttendanceStatus = AttendanceStatus.ATTENDANCE_ON_HOLD ) = Attendance( attendanceId = generateULID(), generation = generation, week = week, member = member, + sessionType = sessionType, attendanceStatus = attendance, attendanceTime = null ) diff --git a/src/main/kotlin/com/depromeet/makers/domain/model/AttendanceStatus.kt b/src/main/kotlin/com/depromeet/makers/domain/model/AttendanceStatus.kt index 1cddc5c..66e116f 100644 --- a/src/main/kotlin/com/depromeet/makers/domain/model/AttendanceStatus.kt +++ b/src/main/kotlin/com/depromeet/makers/domain/model/AttendanceStatus.kt @@ -1,12 +1,13 @@ package com.depromeet.makers.domain.model enum class AttendanceStatus( - val description: String + val description: String, + val point: Double, ) { - ATTENDANCE_ON_HOLD("출석 대기"), - ATTENDANCE("출석"), - ABSENCE("결석"), - TARDY("지각"); + ATTENDANCE_ON_HOLD("출석 대기", 0.0), + ATTENDANCE("출석", 0.0), + ABSENCE("결석", 1.0), + TARDY("지각", 0.5); fun isAttendanceOnHold() = this == ATTENDANCE_ON_HOLD diff --git a/src/main/kotlin/com/depromeet/makers/domain/model/Session.kt b/src/main/kotlin/com/depromeet/makers/domain/model/Session.kt index ada0cc8..0007abd 100644 --- a/src/main/kotlin/com/depromeet/makers/domain/model/Session.kt +++ b/src/main/kotlin/com/depromeet/makers/domain/model/Session.kt @@ -1,5 +1,6 @@ package com.depromeet.makers.domain.model +import com.depromeet.makers.util.generateULID import java.time.LocalDateTime data class Session( @@ -35,4 +36,27 @@ data class Session( place = place, ) } + + companion object { + fun newSession( + generation: Int, + week: Int, + title: String = "세션 제목입니다.", + description: String? = "", + startTime: LocalDateTime = LocalDateTime.now(), + sessionType: SessionType = SessionType.ONLINE, + place: Place = Place.emptyPlace(), + ): Session { + return Session( + sessionId = generateULID(), + generation = generation, + week = week, + title = title, + description = description, + startTime = startTime, + sessionType = sessionType, + place = place, + ) + } + } } diff --git a/src/main/kotlin/com/depromeet/makers/domain/usecase/GetMemberAttendances.kt b/src/main/kotlin/com/depromeet/makers/domain/usecase/GetMemberAttendances.kt index 9950cb6..bef4182 100644 --- a/src/main/kotlin/com/depromeet/makers/domain/usecase/GetMemberAttendances.kt +++ b/src/main/kotlin/com/depromeet/makers/domain/usecase/GetMemberAttendances.kt @@ -2,33 +2,62 @@ package com.depromeet.makers.domain.usecase import com.depromeet.makers.domain.gateway.AttendanceGateway import com.depromeet.makers.domain.gateway.MemberGateway +import com.depromeet.makers.domain.gateway.SessionGateway import com.depromeet.makers.domain.model.Attendance +import com.depromeet.makers.domain.model.Session class GetMemberAttendances( private val attendanceGateway: AttendanceGateway, private val memberGateway: MemberGateway, -) : UseCase> { + private val sessionGateway: SessionGateway, +) : UseCase { data class GetMemberAttendancesInput( val memberId: String, val generation: Int, ) - override fun execute(input: GetMemberAttendancesInput): List { + data class GetMemberAttendancesOutput( + val generation: Int, + val offlineAbsenceScore: Double, + val totalAbsenceScore: Double, + val attendances: List, + ) + + override fun execute(input: GetMemberAttendancesInput): GetMemberAttendancesOutput { val member = memberGateway.getById(input.memberId) - val attendances = attendanceGateway.findAllByMemberIdAndGeneration(member.memberId, input.generation) - .associate { it.week to it } - .toMutableMap() + // 기본값 없으면 세팅 (추후 걷어낼 로직) + val sessions = (1..16).map { + sessionGateway.findByGenerationAndWeek(input.generation, it) ?: sessionGateway.save( + Session.newSession( + generation = input.generation, + week = it, + ) + ) + } - (1..16).filter { week -> !attendances.contains(week) } - .forEach { week -> - attendances[week] = Attendance.newAttendance( + val attendances = (1..16).map { + attendanceGateway.save( + Attendance.newAttendance( member = member, generation = input.generation, - week = week, + week = it, + sessionType = sessions[it - 1].sessionType, ) - } + ) + } - return attendances.values.sortedBy { it.week } + var offlineAbsenceScore = 0.0 + var totalAbsenceScore = 0.0 + (1..16).forEach { week -> + offlineAbsenceScore += if (attendances[week - 1].attendanceStatus.isAbsence() && sessions[week - 1].isOffline()) 1.0 else 0.0 + totalAbsenceScore += attendances[week - 1].attendanceStatus.point + } + return GetMemberAttendancesOutput( + generation = input.generation, + offlineAbsenceScore = offlineAbsenceScore, + totalAbsenceScore = totalAbsenceScore, + attendances = attendances.sortedBy { it.week } + ) } } diff --git a/src/main/kotlin/com/depromeet/makers/infrastructure/db/entity/AttendanceEntity.kt b/src/main/kotlin/com/depromeet/makers/infrastructure/db/entity/AttendanceEntity.kt index fe4e272..d48af64 100644 --- a/src/main/kotlin/com/depromeet/makers/infrastructure/db/entity/AttendanceEntity.kt +++ b/src/main/kotlin/com/depromeet/makers/infrastructure/db/entity/AttendanceEntity.kt @@ -2,6 +2,7 @@ package com.depromeet.makers.infrastructure.db.entity import com.depromeet.makers.domain.model.Attendance import com.depromeet.makers.domain.model.AttendanceStatus +import com.depromeet.makers.domain.model.SessionType import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.EnumType @@ -28,6 +29,10 @@ class AttendanceEntity private constructor( @JoinColumn(name = "member_id", nullable = false) val member: MemberEntity, + @Enumerated(EnumType.STRING) + @Column(name = "session_type", nullable = false) + val sessionType: SessionType, + @Enumerated(EnumType.STRING) @Column(name = "attendance_status", nullable = false) val attendanceStatus: AttendanceStatus, @@ -41,6 +46,7 @@ class AttendanceEntity private constructor( week = week, member = member.toDomain(), attendanceStatus = attendanceStatus, + sessionType = sessionType, attendanceTime = attendanceTime, ) @@ -51,6 +57,7 @@ class AttendanceEntity private constructor( generation = generation, week = week, member = MemberEntity.fromDomain(member), + sessionType = sessionType, attendanceStatus = attendanceStatus, attendanceTime = attendanceTime ) diff --git a/src/main/kotlin/com/depromeet/makers/infrastructure/db/repository/JpaSessionRepository.kt b/src/main/kotlin/com/depromeet/makers/infrastructure/db/repository/JpaSessionRepository.kt index ef21660..aab3c52 100644 --- a/src/main/kotlin/com/depromeet/makers/infrastructure/db/repository/JpaSessionRepository.kt +++ b/src/main/kotlin/com/depromeet/makers/infrastructure/db/repository/JpaSessionRepository.kt @@ -9,7 +9,7 @@ interface JpaSessionRepository : JpaRepository { fun findAllByGeneration(generation: Int): List - fun findByGenerationAndWeek(generation: Int, week: Int): SessionEntity + fun findByGenerationAndWeek(generation: Int, week: Int): SessionEntity? fun findByStartTimeBetween(startTime: LocalDateTime, endTime: LocalDateTime): List } diff --git a/src/main/kotlin/com/depromeet/makers/infrastructure/gateway/SessionGatewayImpl.kt b/src/main/kotlin/com/depromeet/makers/infrastructure/gateway/SessionGatewayImpl.kt index c1ebef7..5f4ac6d 100644 --- a/src/main/kotlin/com/depromeet/makers/infrastructure/gateway/SessionGatewayImpl.kt +++ b/src/main/kotlin/com/depromeet/makers/infrastructure/gateway/SessionGatewayImpl.kt @@ -38,10 +38,10 @@ class SessionGatewayImpl( .map { it.toDomain() } } - override fun findByGenerationAndWeek(generation: Int, week: Int): Session { + override fun findByGenerationAndWeek(generation: Int, week: Int): Session? { return jpaSessionRepository .findByGenerationAndWeek(generation, week) - .toDomain() + ?.toDomain() } override fun findByStartTimeBetween( diff --git a/src/main/kotlin/com/depromeet/makers/presentation/restapi/controller/AttendanceController.kt b/src/main/kotlin/com/depromeet/makers/presentation/restapi/controller/AttendanceController.kt index 759613a..f34a39f 100644 --- a/src/main/kotlin/com/depromeet/makers/presentation/restapi/controller/AttendanceController.kt +++ b/src/main/kotlin/com/depromeet/makers/presentation/restapi/controller/AttendanceController.kt @@ -3,7 +3,7 @@ package com.depromeet.makers.presentation.restapi.controller import com.depromeet.makers.domain.usecase.GetMemberAttendances import com.depromeet.makers.domain.usecase.UpdateAttendance import com.depromeet.makers.presentation.restapi.dto.request.UpdateAttendanceRequest -import com.depromeet.makers.presentation.restapi.dto.response.AttendanceResponse +import com.depromeet.makers.presentation.restapi.dto.response.MyAttendanceResponse import com.depromeet.makers.presentation.restapi.dto.response.UpdateAttendanceResponse import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag @@ -26,14 +26,15 @@ class AttendanceController( fun getMyAttendance( authentication: Authentication, @RequestParam(defaultValue = "15") generation: Int, - ): List { - val attendances = getMemberAttendances.execute( - GetMemberAttendances.GetMemberAttendancesInput( - memberId = authentication.name, - generation = generation, + ): MyAttendanceResponse { + return MyAttendanceResponse.fromDomain( + getMemberAttendances.execute( + GetMemberAttendances.GetMemberAttendancesInput( + memberId = authentication.name, + generation = generation, + ) ) ) - return attendances.map { AttendanceResponse.fromDomain(it) } } @PreAuthorize("hasRole('ORGANIZER')") diff --git a/src/main/kotlin/com/depromeet/makers/presentation/restapi/dto/response/AttendanceResponse.kt b/src/main/kotlin/com/depromeet/makers/presentation/restapi/dto/response/AttendanceResponse.kt index 3172589..6844f38 100644 --- a/src/main/kotlin/com/depromeet/makers/presentation/restapi/dto/response/AttendanceResponse.kt +++ b/src/main/kotlin/com/depromeet/makers/presentation/restapi/dto/response/AttendanceResponse.kt @@ -2,6 +2,7 @@ package com.depromeet.makers.presentation.restapi.dto.response import com.depromeet.makers.domain.model.Attendance import com.depromeet.makers.domain.model.AttendanceStatus +import com.depromeet.makers.domain.model.SessionType import io.swagger.v3.oas.annotations.media.Schema import java.time.LocalDateTime @@ -19,6 +20,9 @@ data class AttendanceResponse( @Schema(description = "회원 ID", example = "01HWPNRE5TS9S7VC99WPETE5KE") val memberId: String, + @Schema(description = "세션 타입", example = "ONLINE") + val sessionType: SessionType, + @Schema(description = "출석 상태", example = "ATTENDANCE") val attendanceStatus: AttendanceStatus, @@ -32,6 +36,7 @@ data class AttendanceResponse( generation = generation, week = week, memberId = member.memberId, + sessionType = sessionType, attendanceStatus = attendanceStatus, attendanceTime = attendanceTime, ) diff --git a/src/main/kotlin/com/depromeet/makers/presentation/restapi/dto/response/MyAttendanceResponse.kt b/src/main/kotlin/com/depromeet/makers/presentation/restapi/dto/response/MyAttendanceResponse.kt new file mode 100644 index 0000000..7e9ac55 --- /dev/null +++ b/src/main/kotlin/com/depromeet/makers/presentation/restapi/dto/response/MyAttendanceResponse.kt @@ -0,0 +1,27 @@ +package com.depromeet.makers.presentation.restapi.dto.response + +import com.depromeet.makers.domain.usecase.GetMemberAttendances +import io.swagger.v3.oas.annotations.media.Schema + +data class MyAttendanceResponse( + + @Schema(description = "기수", example = "15") + val generation: Int, + + @Schema(description = "오프라인 결석 점수", example = "1.0") + val offlineAbsenceScore: Double, + + @Schema(description = "총 결석 점수 (지각: 0.5)", example = "2.5") + val totalAbsenceScore: Double, + + val attendances: List +) { + companion object { + fun fromDomain(getMemberAttendancesOutput: GetMemberAttendances.GetMemberAttendancesOutput) = MyAttendanceResponse( + generation = getMemberAttendancesOutput.generation, + offlineAbsenceScore = getMemberAttendancesOutput.offlineAbsenceScore, + totalAbsenceScore = getMemberAttendancesOutput.totalAbsenceScore, + attendances = getMemberAttendancesOutput.attendances.map { AttendanceResponse.fromDomain(it) } + ) + } +}