Skip to content

Commit 72a560a

Browse files
authored
Merge pull request #642 from DSM-PICK/develop
develop merge into main
2 parents e24d9db + 05b73f7 commit 72a560a

File tree

12 files changed

+133
-29
lines changed

12 files changed

+133
-29
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package dsm.pick2024.domain.application.exception
2+
3+
import dsm.pick2024.global.error.exception.ErrorCode
4+
import dsm.pick2024.global.error.exception.PickException
5+
6+
object InvalidApplicationKindException : PickException(
7+
ErrorCode.INVALID_APPLICATION_KIND
8+
)

src/main/kotlin/dsm/pick2024/domain/application/service/ApplicationService.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import dsm.pick2024.domain.application.port.`in`.ApplicationUseCase
99
import dsm.pick2024.domain.application.port.out.ExistsApplicationPort
1010
import dsm.pick2024.domain.application.port.out.SaveApplicationPort
1111
import dsm.pick2024.domain.application.presentation.dto.request.ApplicationRequest
12+
import dsm.pick2024.domain.attendance.domain.service.AttendanceService
1213
import dsm.pick2024.domain.fcm.dto.request.FcmRequest
1314
import dsm.pick2024.domain.main.port.`in`.MainUseCase
1415
import dsm.pick2024.domain.outbox.domain.Outbox
@@ -27,7 +28,8 @@ class ApplicationService(
2728
private val userFacadeUseCase: UserFacadeUseCase,
2829
private val adminFinderUseCase: AdminFinderUseCase,
2930
private val saveOutboxPort: SaveOutboxPort,
30-
private val mainUseCase: MainUseCase
31+
private val mainUseCase: MainUseCase,
32+
private val attendanceService: AttendanceService
3133
) : ApplicationUseCase {
3234

3335
@Transactional
@@ -37,6 +39,12 @@ class ApplicationService(
3739
throw AlreadyApplyingForPicnicException
3840
}
3941

42+
attendanceService.checkApplicationTime(
43+
applicationType = request.applicationType,
44+
start = request.start,
45+
end = request.end
46+
)
47+
4048
saveApplicationPort.save(
4149
Application(
4250
userName = user.name,

src/main/kotlin/dsm/pick2024/domain/application/service/ChangeApplicationStatusService.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package dsm.pick2024.domain.application.service
22

33
import dsm.pick2024.domain.admin.port.`in`.AdminFacadeUseCase
4+
import dsm.pick2024.domain.application.enums.ApplicationKind
45
import dsm.pick2024.domain.application.enums.Status
6+
import dsm.pick2024.domain.application.exception.InvalidApplicationKindException
57
import dsm.pick2024.domain.application.exception.AlreadyApplyingForPicnicException
68
import dsm.pick2024.domain.application.port.`in`.ApplicationFinderUseCase
79
import dsm.pick2024.domain.application.port.`in`.ChangeApplicationStatusUseCase
@@ -26,7 +28,12 @@ class ChangeApplicationStatusService(
2628
val admin = adminFacadeUseCase.currentAdmin()
2729
val applications = request.idList.map { applicationFinderUseCase.findByIdOrThrow(it) }
2830
applications.forEach {
29-
if (it.status != Status.QUIET) throw AlreadyApplyingForPicnicException
31+
if (it.applicationKind != ApplicationKind.APPLICATION) {
32+
throw InvalidApplicationKindException
33+
}
34+
if (it.status != Status.QUIET) {
35+
throw AlreadyApplyingForPicnicException
36+
}
3037
}
3138
val deviceTokens = applications.mapNotNull {
3239
userFacadeUseCase.getUserById(

src/main/kotlin/dsm/pick2024/domain/attendance/domain/service/AttendanceService.kt

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dsm.pick2024.domain.attendance.domain.service
33
import dsm.pick2024.domain.application.enums.ApplicationType
44
import dsm.pick2024.domain.attendance.domain.Attendance
55
import dsm.pick2024.domain.attendance.enums.AttendanceStatus
6+
import dsm.pick2024.domain.attendance.exception.InvalidPeriodException
67
import org.springframework.stereotype.Component
78
import java.time.LocalTime
89

@@ -14,7 +15,7 @@ class AttendanceService {
1415
LocalTime.of(8, 40) to LocalTime.of(9, 40), // 1교시
1516
LocalTime.of(9, 40) to LocalTime.of(10, 40), // 2교시
1617
LocalTime.of(10, 40) to LocalTime.of(11, 40), // 3교시
17-
LocalTime.of(12, 40) to LocalTime.of(13, 30), // 4교시
18+
LocalTime.of(11, 40) to LocalTime.of(13, 30), // 4교시
1819
LocalTime.of(13, 30) to LocalTime.of(14, 40), // 5교시
1920
LocalTime.of(14, 30) to LocalTime.of(15, 30), // 6교시
2021
LocalTime.of(15, 30) to LocalTime.of(16, 30), // 7교시
@@ -29,17 +30,70 @@ class AttendanceService {
2930
}
3031

3132
// 교시 혹은 시간을 기반으로 교시 목록을 반환하는 함수
32-
fun translateApplication(start: String, end: String?, applicationType: ApplicationType): List<String> {
33+
fun translateApplication(start: String, end: String, applicationType: ApplicationType): Pair<String, String> {
3334
return when (applicationType) {
34-
ApplicationType.PERIOD -> listOf(start, end!!)
35+
ApplicationType.PERIOD -> Pair(start, end)
3536
ApplicationType.TIME -> {
3637
val startTime = LocalTime.parse(start)
37-
val endTime = end?.let { LocalTime.parse(it) }
38+
val endTime = LocalTime.parse(end)
3839
getMatchPeriods(startTime, endTime)
3940
}
4041
}
4142
}
4243

44+
fun checkApplicationTime(applicationType: ApplicationType, start: String, end: String) {
45+
when (applicationType) {
46+
ApplicationType.TIME -> {
47+
val startTime = LocalTime.parse(start)
48+
val endTime = LocalTime.parse(end)
49+
if (startTime > endTime ||
50+
endTime > LocalTime.of(20, 30) ||
51+
startTime < LocalTime.of(8, 30)
52+
) {
53+
throw InvalidPeriodException
54+
}
55+
}
56+
ApplicationType.PERIOD -> {
57+
val startPeriod = start.replace("교시", "").toIntOrNull() ?: throw InvalidPeriodException
58+
val endPeriod = end.replace("교시", "").toIntOrNull() ?: throw InvalidPeriodException
59+
if (startPeriod > endPeriod || startPeriod < 1 || endPeriod > 10) {
60+
throw InvalidPeriodException
61+
}
62+
}
63+
}
64+
}
65+
66+
fun checkEarlyReturnTime(applicationType: ApplicationType, start: String) {
67+
when (applicationType) {
68+
ApplicationType.TIME -> {
69+
val startTime = LocalTime.parse(start)
70+
if (startTime < LocalTime.of(8, 30)) {
71+
throw InvalidPeriodException
72+
}
73+
}
74+
ApplicationType.PERIOD -> {
75+
val startPeriod = start.replace("교시", "").toInt()
76+
if (startPeriod < 1 || startPeriod > 10) {
77+
throw InvalidPeriodException
78+
}
79+
}
80+
}
81+
}
82+
83+
fun translateEarlyReturn(start: String, applicationType: ApplicationType): String {
84+
return when (applicationType) {
85+
ApplicationType.PERIOD -> start
86+
ApplicationType.TIME -> {
87+
val startTime = LocalTime.parse(start)
88+
val startIndex = periods.indexOfFirst { (start, endAt) ->
89+
startTime >= start && startTime < endAt
90+
}
91+
if (startIndex == -1) throw InvalidPeriodException
92+
periodNames[startIndex]
93+
}
94+
}
95+
}
96+
4397
// 주어진 교시 혹은 시간에 해당하는 출석 상태를 업데이트하는 함수
4498
fun updateAttendanceToApplication(
4599
start: String,
@@ -86,10 +140,19 @@ class AttendanceService {
86140

87141
fun updateAttendanceToEarlyReturn(
88142
start: String,
143+
applicationType: ApplicationType,
89144
attendance: Attendance
90145
): Attendance {
91-
val startTime = LocalTime.parse(start)
92-
val list = periods.filter { it.first.isAfter(startTime) || it.first == startTime }
146+
val list = when (applicationType) {
147+
ApplicationType.PERIOD -> {
148+
val startIndex = periodNames.indexOf(start).coerceAtLeast(0)
149+
periods.subList(startIndex, periods.size)
150+
}
151+
ApplicationType.TIME -> {
152+
val startTime = LocalTime.parse(start)
153+
periods.filter { startTime < it.second }
154+
}
155+
}
93156
var updateAttendance = attendance
94157
list.forEach { period ->
95158
updateAttendance = when (period) {
@@ -104,14 +167,16 @@ class AttendanceService {
104167
return updateAttendance
105168
}
106169

107-
private fun getMatchPeriods(startTime: LocalTime, endTime: LocalTime?): List<String> {
108-
return periods
109-
.mapIndexed { index, period ->
110-
if ((startTime < period.second) && (endTime == null || endTime > period.first)) {
111-
periodNames[index]
112-
} else {
113-
null
114-
}
115-
}.filterNotNull()
170+
private fun getMatchPeriods(startTime: LocalTime, endTime: LocalTime): Pair<String, String> {
171+
val startIndex = periods.indexOfFirst { (start, endAt) ->
172+
startTime in start..endAt
173+
}
174+
val endIndex = periods.indexOfFirst { (start, endAt) ->
175+
endTime in start..endAt
176+
}
177+
178+
if (startIndex == -1 || endIndex == -1) throw InvalidPeriodException
179+
180+
return periodNames[startIndex] to periodNames[endIndex]
116181
}
117182
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package dsm.pick2024.domain.earlyreturn.presentation.dto.request
22

3+
import dsm.pick2024.domain.application.enums.ApplicationType
34
import dsm.pick2024.global.annotation.ValidFormat
45

56
@ValidFormat
67
data class CreateEarlyReturnRequest(
78
val reason: String,
8-
val start: String
9+
val start: String,
10+
val applicationType: ApplicationType
911
)

src/main/kotlin/dsm/pick2024/domain/earlyreturn/service/CreateEarlyReturnService.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ package dsm.pick2024.domain.earlyreturn.service
33
import dsm.pick2024.domain.admin.port.`in`.AdminFinderUseCase
44
import dsm.pick2024.domain.application.domain.Application
55
import dsm.pick2024.domain.application.enums.ApplicationKind
6-
import dsm.pick2024.domain.application.enums.ApplicationType
76
import dsm.pick2024.domain.application.enums.Status
87
import dsm.pick2024.domain.application.port.out.ExistsApplicationPort
98
import dsm.pick2024.domain.application.port.out.SaveApplicationPort
9+
import dsm.pick2024.domain.attendance.domain.service.AttendanceService
1010
import dsm.pick2024.domain.earlyreturn.exception.AlreadyApplyingForEarlyReturnException
1111
import dsm.pick2024.domain.earlyreturn.port.`in`.CreateEarlyReturnUseCase
1212
import dsm.pick2024.domain.earlyreturn.presentation.dto.request.CreateEarlyReturnRequest
@@ -25,7 +25,8 @@ class CreateEarlyReturnService(
2525
private val userFacadeUseCase: UserFacadeUseCase,
2626
private val adminFinderUseCase: AdminFinderUseCase,
2727
private val outboxFacadeUseCase: OutboxFacadeUseCase,
28-
private val mainUseCase: MainUseCase
28+
private val mainUseCase: MainUseCase,
29+
private val attendanceService: AttendanceService
2930
) : CreateEarlyReturnUseCase {
3031
@Transactional
3132
override fun createEarlyReturn(request: CreateEarlyReturnRequest) {
@@ -35,6 +36,8 @@ class CreateEarlyReturnService(
3536
throw AlreadyApplyingForEarlyReturnException
3637
}
3738

39+
attendanceService.checkEarlyReturnTime(request.applicationType, request.start)
40+
3841
saveApplicationPort.save(
3942
Application(
4043
userName = user.name,
@@ -46,7 +49,7 @@ class CreateEarlyReturnService(
4649
classNum = user.classNum,
4750
num = user.num,
4851
userId = user.id,
49-
applicationType = ApplicationType.TIME,
52+
applicationType = request.applicationType,
5053
applicationKind = ApplicationKind.EARLY_RETURN
5154
)
5255
)

src/main/kotlin/dsm/pick2024/domain/earlyreturn/service/processor/EarlyReturnApprovalProcessor.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package dsm.pick2024.domain.earlyreturn.service.processor
22

33
import dsm.pick2024.domain.application.domain.Application
4-
import dsm.pick2024.domain.application.enums.ApplicationType
54
import dsm.pick2024.domain.application.enums.Status
65
import dsm.pick2024.domain.application.port.out.SaveApplicationPort
76
import dsm.pick2024.domain.applicationstory.domain.ApplicationStory
@@ -37,7 +36,7 @@ class EarlyReturnApprovalProcessor(
3736

3837
val attendances = updateEarlyReturnList.map {
3938
val attendanceId = attendanceFinderUseCase.findByUserIdOrThrow(it.userId)
40-
attendanceService.updateAttendanceToEarlyReturn(it.start, attendanceId)
39+
attendanceService.updateAttendanceToEarlyReturn(it.start, it.applicationType, attendanceId)
4140
}.toMutableList()
4241

4342
saveApplicationPort.saveAll(updateEarlyReturnList)
@@ -50,11 +49,11 @@ class EarlyReturnApprovalProcessor(
5049
}
5150

5251
private fun createApplicationStory(application: Application): ApplicationStory {
53-
val start = attendanceService.translateApplication(application.start, null, ApplicationType.TIME)
52+
val start = attendanceService.translateEarlyReturn(application.start, application.applicationType)
5453
return ApplicationStory(
5554
reason = application.reason,
5655
userName = application.userName,
57-
start = start.first(),
56+
start = start,
5857
date = application.date,
5958
type = Type.EARLY_RETURN,
6059
userId = application.userId

src/main/kotlin/dsm/pick2024/global/annotation/common/FormatValidator.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ class FormatValidator : ConstraintValidator<ValidFormat, Any> {
2828
}
2929

3030
private fun validateCreateEarlyReturnRequest(request: CreateEarlyReturnRequest): Boolean {
31-
return isValidTimeFormat(request.start)
31+
return if (request.applicationType == ApplicationType.PERIOD) {
32+
isValidPeriodFormat(request.start)
33+
} else {
34+
isValidTimeFormat(request.start)
35+
}
3236
}
3337

3438
private fun isValidPeriodFormat(period: String): Boolean {

src/main/kotlin/dsm/pick2024/global/config/async/AsyncConfig.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import java.util.concurrent.RejectedExecutionException
2424
* - 최대 5회 재시도
2525
* - 지수 백오프 (2초 → 4초 → 8초 → 16초 → 30초)
2626
*/
27-
2827
@Configuration
2928
@EnableAsync
3029
@EnableRetry

src/main/kotlin/dsm/pick2024/global/config/debezium/DebeziumProperties.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ data class DebeziumProperties(
1515
data class ConnectorConfig(
1616
val name: String,
1717
val connectorClass: String = "io.debezium.connector.mysql.MySqlConnector",
18-
val tasksMax: Int = 1
18+
val tasksMax: Int = 1,
19+
val timePrecisionMode: String = "connect"
1920
)
2021

2122
data class DatabaseConfig(
@@ -25,7 +26,8 @@ data class DebeziumProperties(
2526
val password: String,
2627
val serverId: String,
2728
val serverName: String,
28-
val includeList: String
29+
val includeList: String,
30+
val serverTimeZone: String = "Asia/Seoul"
2931
)
3032

3133
data class SchemaConfig(
@@ -52,6 +54,9 @@ data class DebeziumProperties(
5254
"connector.class" to connector.connectorClass,
5355
"tasks.max" to connector.tasksMax.toString(),
5456

57+
"time.precision.mode" to connector.timePrecisionMode,
58+
59+
"database.server.timezone" to database.serverTimeZone,
5560
"database.hostname" to database.hostname,
5661
"database.port" to database.port.toString(),
5762
"database.user" to database.user,

0 commit comments

Comments
 (0)