From 5f93e0c673df3d9721dce9823a13ce64229ce862 Mon Sep 17 00:00:00 2001 From: ddingmin Date: Fri, 17 May 2024 00:44:44 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EB=82=B4=20=EC=B6=9C=EC=84=9D=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EC=97=90=20=EA=B2=B0=EC=84=9D=20=ED=98=84?= =?UTF-8?q?=ED=99=A9=20=EC=9D=91=EB=8B=B5=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../makers/domain/model/AttendanceStatus.kt | 11 +++--- .../domain/usecase/GetMemberAttendances.kt | 39 +++++++++++++++---- .../controller/AttendanceController.kt | 15 +++---- .../dto/response/MyAttendanceResponse.kt | 27 +++++++++++++ 4 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 src/main/kotlin/com/depromeet/makers/presentation/restapi/dto/response/MyAttendanceResponse.kt 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/usecase/GetMemberAttendances.kt b/src/main/kotlin/com/depromeet/makers/domain/usecase/GetMemberAttendances.kt index 9950cb6..76d745d 100644 --- a/src/main/kotlin/com/depromeet/makers/domain/usecase/GetMemberAttendances.kt +++ b/src/main/kotlin/com/depromeet/makers/domain/usecase/GetMemberAttendances.kt @@ -2,18 +2,27 @@ 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 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) @@ -22,13 +31,29 @@ class GetMemberAttendances( (1..16).filter { week -> !attendances.contains(week) } .forEach { week -> - attendances[week] = Attendance.newAttendance( - member = member, - generation = input.generation, - week = week, + attendances[week] = attendanceGateway.save( + Attendance.newAttendance( + member = member, + generation = input.generation, + week = week, + ) ) } - return attendances.values.sortedBy { it.week } + var offlineAbsenceScore = 0.0 + var totalAbsenceScore = 0.0 + attendances.values.forEach { + offlineAbsenceScore += if (it.attendanceStatus.isAbsence() && + runCatching { sessionGateway.findByGenerationAndWeek(input.generation, it.week).isOffline() } + .getOrDefault(false) + ) 1.0 else 0.0 + totalAbsenceScore += it.attendanceStatus.point + } + return GetMemberAttendancesOutput( + generation = input.generation, + offlineAbsenceScore = offlineAbsenceScore, + totalAbsenceScore = totalAbsenceScore, + attendances = attendances.values.sortedBy { it.week } + ) } } 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/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) } + ) + } +} From 91fb5f6e849357fde35c4312abd76d16e2f370aa Mon Sep 17 00:00:00 2001 From: ddingmin Date: Fri, 17 May 2024 01:31:01 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20attendance=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EC=97=90=20=EC=98=A8,=20=EC=98=A4=ED=94=84=EB=9D=BC?= =?UTF-8?q?=EC=9D=B8=20=EC=A0=95=EB=B3=B4=20=EC=B6=94=EA=B0=80=20/=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EA=B0=92=20=EC=84=B8=ED=8C=85=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../makers/domain/gateway/SessionGateway.kt | 2 +- .../makers/domain/model/Attendance.kt | 3 ++ .../depromeet/makers/domain/model/Session.kt | 24 +++++++++++ .../domain/usecase/GetMemberAttendances.kt | 42 ++++++++++--------- .../db/entity/AttendanceEntity.kt | 2 + .../db/repository/JpaSessionRepository.kt | 2 +- .../gateway/SessionGatewayImpl.kt | 4 +- .../dto/response/AttendanceResponse.kt | 5 +++ 8 files changed, 61 insertions(+), 23 deletions(-) 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/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 76d745d..bef4182 100644 --- a/src/main/kotlin/com/depromeet/makers/domain/usecase/GetMemberAttendances.kt +++ b/src/main/kotlin/com/depromeet/makers/domain/usecase/GetMemberAttendances.kt @@ -4,6 +4,7 @@ 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, @@ -25,35 +26,38 @@ class GetMemberAttendances( 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] = attendanceGateway.save( - Attendance.newAttendance( - member = member, - generation = input.generation, - week = week, - ) + val attendances = (1..16).map { + attendanceGateway.save( + Attendance.newAttendance( + member = member, + generation = input.generation, + week = it, + sessionType = sessions[it - 1].sessionType, ) - } + ) + } var offlineAbsenceScore = 0.0 var totalAbsenceScore = 0.0 - attendances.values.forEach { - offlineAbsenceScore += if (it.attendanceStatus.isAbsence() && - runCatching { sessionGateway.findByGenerationAndWeek(input.generation, it.week).isOffline() } - .getOrDefault(false) - ) 1.0 else 0.0 - totalAbsenceScore += it.attendanceStatus.point + (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.values.sortedBy { it.week } + 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..0a7c3c9 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 @@ -41,6 +42,7 @@ class AttendanceEntity private constructor( week = week, member = member.toDomain(), attendanceStatus = attendanceStatus, + sessionType = SessionType.ONLINE, 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/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, ) From de82772aacaa7dc8a75983c9291a27154a914883 Mon Sep 17 00:00:00 2001 From: ddingmin Date: Fri, 17 May 2024 01:39:19 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20attendanceEntity=20sessionType=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../makers/infrastructure/db/entity/AttendanceEntity.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 0a7c3c9..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 @@ -29,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, @@ -42,7 +46,7 @@ class AttendanceEntity private constructor( week = week, member = member.toDomain(), attendanceStatus = attendanceStatus, - sessionType = SessionType.ONLINE, + sessionType = sessionType, attendanceTime = attendanceTime, ) @@ -53,6 +57,7 @@ class AttendanceEntity private constructor( generation = generation, week = week, member = MemberEntity.fromDomain(member), + sessionType = sessionType, attendanceStatus = attendanceStatus, attendanceTime = attendanceTime )