From 866032a2d73900cf7770ee0233ae7de408e0e33b Mon Sep 17 00:00:00 2001 From: soo0711 Date: Thu, 19 Feb 2026 21:30:11 +0900 Subject: [PATCH 01/62] =?UTF-8?q?test:=20Schedule=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=95=88=EC=A0=84=EB=A7=9D=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/AttendanceUseCaseImplTest.kt | 54 +++++ .../usecase/MeetingUseCaseImplTest.kt | 196 ++++++++++++++++++ .../usecase/ScheduleUseCaseImplTest.kt | 52 +++++ .../schedule/domain/entity/MeetingTest.kt | 17 ++ .../schedule/fixture/ScheduleTestFixture.kt | 69 ++++-- 5 files changed, 369 insertions(+), 19 deletions(-) create mode 100644 src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt create mode 100644 src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt create mode 100644 src/test/kotlin/com/weeth/domain/schedule/domain/entity/MeetingTest.kt diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImplTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImplTest.kt index 6f41a783..1803e623 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImplTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImplTest.kt @@ -284,4 +284,58 @@ class AttendanceUseCaseImplTest : } } } + + describe("findAllAttendanceByMeeting") { + it("해당 정기모임의 출석 목록을 매핑하여 반환한다") { + val meetingId = 1L + val meeting = createOneDayMeeting(LocalDate.now(), 1, 1234, "Today") + val attendance1 = mockk() + val attendance2 = mockk() + val info1 = mockk() + val info2 = mockk() + + every { meetingGetService.find(meetingId) } returns meeting + every { attendanceGetService.findAllByMeeting(meeting) } returns listOf(attendance1, attendance2) + every { attendanceMapper.toAttendanceInfoDto(attendance1) } returns info1 + every { attendanceMapper.toAttendanceInfoDto(attendance2) } returns info2 + + val result = attendanceUseCase.findAllAttendanceByMeeting(meetingId) + + result.size shouldBe 2 + result[0] shouldBe info1 + result[1] shouldBe info2 + } + } + + describe("updateAttendanceStatus") { + it("ABSENT로 변경 시 close + removeAttend + absent 호출") { + val attendance = mockk(relaxUnitFun = true) + val user = mockk(relaxUnitFun = true) + every { attendanceGetService.findByAttendanceId(1L) } returns attendance + every { attendance.user } returns user + + attendanceUseCase.updateAttendanceStatus( + listOf(AttendanceDTO.UpdateStatus(1L, "ABSENT")), + ) + + verify { attendance.close() } + verify { user.removeAttend() } + verify { user.absent() } + } + + it("ATTEND로 변경 시 attend + removeAbsent + attend 호출") { + val attendance = mockk(relaxUnitFun = true) + val user = mockk(relaxUnitFun = true) + every { attendanceGetService.findByAttendanceId(2L) } returns attendance + every { attendance.user } returns user + + attendanceUseCase.updateAttendanceStatus( + listOf(AttendanceDTO.UpdateStatus(2L, "ATTEND")), + ) + + verify { attendance.attend() } + verify { user.removeAbsent() } + verify { user.attend() } + } + } }) diff --git a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt new file mode 100644 index 00000000..3906ad75 --- /dev/null +++ b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt @@ -0,0 +1,196 @@ +package com.weeth.domain.schedule.application.usecase + +import com.weeth.domain.attendance.domain.entity.Attendance +import com.weeth.domain.attendance.domain.entity.enums.Status +import com.weeth.domain.attendance.domain.service.AttendanceDeleteService +import com.weeth.domain.attendance.domain.service.AttendanceGetService +import com.weeth.domain.attendance.domain.service.AttendanceSaveService +import com.weeth.domain.attendance.domain.service.AttendanceUpdateService +import com.weeth.domain.schedule.application.dto.MeetingDTO +import com.weeth.domain.schedule.application.dto.ScheduleDTO +import com.weeth.domain.schedule.application.mapper.MeetingMapper +import com.weeth.domain.schedule.domain.entity.Meeting +import com.weeth.domain.schedule.domain.entity.enums.Type +import com.weeth.domain.schedule.domain.service.MeetingDeleteService +import com.weeth.domain.schedule.domain.service.MeetingGetService +import com.weeth.domain.schedule.domain.service.MeetingSaveService +import com.weeth.domain.schedule.domain.service.MeetingUpdateService +import com.weeth.domain.schedule.fixture.ScheduleTestFixture +import com.weeth.domain.user.domain.entity.Cardinal +import com.weeth.domain.user.domain.entity.User +import com.weeth.domain.user.domain.entity.enums.Role +import com.weeth.domain.user.domain.service.CardinalGetService +import com.weeth.domain.user.domain.service.UserGetService +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.clearMocks +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import jakarta.persistence.EntityManager +import org.springframework.test.util.ReflectionTestUtils +import java.time.LocalDateTime + +class MeetingUseCaseImplTest : + DescribeSpec({ + val meetingGetService = mockk() + val meetingMapper = mockk() + val meetingSaveService = mockk(relaxUnitFun = true) + val userGetService = mockk() + val meetingUpdateService = mockk(relaxUnitFun = true) + val meetingDeleteService = mockk(relaxUnitFun = true) + val attendanceGetService = mockk() + val attendanceSaveService = mockk(relaxUnitFun = true) + val attendanceDeleteService = mockk(relaxUnitFun = true) + val attendanceUpdateService = mockk(relaxUnitFun = true) + val cardinalGetService = mockk() + val em = mockk(relaxUnitFun = true) + + val useCase = + MeetingUseCaseImpl( + meetingGetService, + meetingMapper, + meetingSaveService, + userGetService, + meetingUpdateService, + meetingDeleteService, + attendanceGetService, + attendanceSaveService, + attendanceDeleteService, + attendanceUpdateService, + cardinalGetService, + ) + + beforeSpec { + ReflectionTestUtils.setField(useCase, "em", em) + } + + beforeTest { + clearMocks( + meetingGetService, meetingMapper, meetingSaveService, userGetService, + meetingUpdateService, meetingDeleteService, attendanceGetService, + attendanceSaveService, attendanceDeleteService, attendanceUpdateService, + cardinalGetService, em, + ) + } + + describe("find(userId, meetingId)") { + val meetingId = 1L + val userId = 10L + val meeting = ScheduleTestFixture.createMeeting(id = meetingId) + + context("ADMIN 유저일 때") { + it("toAdminResponse로 매핑한다") { + val adminUser = mockk() + every { adminUser.role } returns Role.ADMIN + every { userGetService.find(userId) } returns adminUser + every { meetingGetService.find(meetingId) } returns meeting + val adminResponse = mockk() + every { meetingMapper.toAdminResponse(meeting) } returns adminResponse + + val result = useCase.find(userId, meetingId) + + result shouldBe adminResponse + verify { meetingMapper.toAdminResponse(meeting) } + } + } + + context("일반 유저일 때") { + it("to(meeting)으로 매핑한다 (코드 미노출)") { + val normalUser = mockk() + every { normalUser.role } returns Role.USER + every { userGetService.find(userId) } returns normalUser + every { meetingGetService.find(meetingId) } returns meeting + val normalResponse = mockk() + every { meetingMapper.to(meeting) } returns normalResponse + + val result = useCase.find(userId, meetingId) + + result shouldBe normalResponse + verify { meetingMapper.to(meeting) } + } + } + } + + describe("find(cardinal)") { + it("이번 주 정기모임이 있으면 thisWeek에 포함된다") { + val now = LocalDateTime.now() + val thisWeekMeeting = + ScheduleTestFixture.createMeeting( + id = 1L, + title = "This Week", + start = now, + end = now.plusHours(2), + ) + val lastWeekMeeting = + ScheduleTestFixture.createMeeting( + id = 2L, + title = "Last Week", + start = now.minusDays(14), + end = now.minusDays(14).plusHours(2), + ) + val thisWeekInfo = MeetingDTO.Info(1L, 1, "This Week", now) + val lastWeekInfo = MeetingDTO.Info(2L, 1, "Last Week", now.minusDays(14)) + + every { meetingGetService.findMeetingByCardinal(1) } returns listOf(thisWeekMeeting, lastWeekMeeting) + every { meetingMapper.toInfo(thisWeekMeeting) } returns thisWeekInfo + every { meetingMapper.toInfo(lastWeekMeeting) } returns lastWeekInfo + + val result = useCase.find(1) + + result.thisWeek shouldNotBe null + result.thisWeek.title() shouldBe "This Week" + } + } + + describe("save") { + it("정기모임 저장 후 해당 기수 전체 유저에게 출석을 생성한다") { + val userId = 10L + val user = mockk() + val cardinal = mockk() + val userList = listOf(mockk(), mockk(), mockk()) + val meeting = ScheduleTestFixture.createMeeting() + val dto = + ScheduleDTO.Save( + "Title", "Content", "Location", null, + Type.MEETING, 1, + LocalDateTime.of(2026, 3, 1, 10, 0), + LocalDateTime.of(2026, 3, 1, 12, 0), + ) + + every { userGetService.find(userId) } returns user + every { cardinalGetService.findByUserSide(1) } returns cardinal + every { userGetService.findAllByCardinal(cardinal) } returns userList + every { meetingMapper.from(dto, user) } returns meeting + + useCase.save(dto, userId) + + verify(exactly = 1) { meetingSaveService.save(meeting) } + verify(exactly = 1) { attendanceSaveService.saveAll(userList, meeting) } + } + } + + describe("delete") { + it("출석 통계 롤백 후 출석 삭제 → 정기모임 삭제 순서로 처리한다") { + val meetingId = 1L + val meeting = ScheduleTestFixture.createMeeting(id = meetingId) + val attendance1 = mockk() + val attendance2 = mockk() + val attendances = listOf(attendance1, attendance2) + + every { meetingGetService.find(meetingId) } returns meeting + every { attendanceGetService.findAllByMeeting(meeting) } returns attendances + + useCase.delete(meetingId) + + verify(ordering = io.mockk.Ordering.ORDERED) { + attendanceUpdateService.updateUserAttendanceByStatus(attendances) + em.flush() + em.clear() + attendanceDeleteService.deleteAll(meeting) + meetingDeleteService.delete(meeting) + } + } + } + }) diff --git a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt new file mode 100644 index 00000000..bff8fe35 --- /dev/null +++ b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt @@ -0,0 +1,52 @@ +package com.weeth.domain.schedule.application.usecase + +import com.weeth.domain.schedule.application.dto.ScheduleDTO +import com.weeth.domain.schedule.domain.service.EventGetService +import com.weeth.domain.schedule.domain.service.MeetingGetService +import com.weeth.domain.user.domain.service.CardinalGetService +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe +import io.mockk.clearMocks +import io.mockk.every +import io.mockk.mockk +import java.time.LocalDateTime + +class ScheduleUseCaseImplTest : + DescribeSpec({ + val eventGetService = mockk() + val meetingGetService = mockk() + val cardinalGetService = mockk() + val useCase = ScheduleUseCaseImpl(eventGetService, meetingGetService, cardinalGetService) + + beforeTest { + clearMocks(eventGetService, meetingGetService, cardinalGetService) + } + + describe("findByMonthly") { + val start = LocalDateTime.of(2026, 3, 1, 0, 0) + val end = LocalDateTime.of(2026, 3, 31, 23, 59) + + it("Event와 Meeting을 합쳐서 start 기준 오름차순 정렬한다") { + val event1 = ScheduleDTO.Response(1L, "Event", start.plusDays(2), start.plusDays(3), false) + val meeting1 = ScheduleDTO.Response(2L, "Meeting", start.plusDays(1), start.plusDays(1), true) + + every { eventGetService.find(start, end) } returns listOf(event1) + every { meetingGetService.find(start, end) } returns listOf(meeting1) + + val result = useCase.findByMonthly(start, end) + + result.size shouldBe 2 + result[0].title() shouldBe "Meeting" + result[1].title() shouldBe "Event" + } + + it("Event와 Meeting 모두 없으면 빈 리스트를 반환한다") { + every { eventGetService.find(start, end) } returns emptyList() + every { meetingGetService.find(start, end) } returns emptyList() + + val result = useCase.findByMonthly(start, end) + + result.size shouldBe 0 + } + } + }) diff --git a/src/test/kotlin/com/weeth/domain/schedule/domain/entity/MeetingTest.kt b/src/test/kotlin/com/weeth/domain/schedule/domain/entity/MeetingTest.kt new file mode 100644 index 00000000..88d1f232 --- /dev/null +++ b/src/test/kotlin/com/weeth/domain/schedule/domain/entity/MeetingTest.kt @@ -0,0 +1,17 @@ +package com.weeth.domain.schedule.domain.entity + +import com.weeth.domain.schedule.domain.entity.enums.MeetingStatus +import com.weeth.domain.schedule.fixture.ScheduleTestFixture +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe + +class MeetingTest : + StringSpec({ + "close는 meetingStatus를 CLOSE로 변경한다" { + val meeting = ScheduleTestFixture.createMeeting(meetingStatus = MeetingStatus.OPEN) + + meeting.close() + + meeting.meetingStatus shouldBe MeetingStatus.CLOSE + } + }) diff --git a/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt b/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt index 8c1b03d0..8adc4a60 100644 --- a/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt +++ b/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt @@ -2,27 +2,58 @@ package com.weeth.domain.schedule.fixture import com.weeth.domain.schedule.domain.entity.Event import com.weeth.domain.schedule.domain.entity.Meeting +import com.weeth.domain.schedule.domain.entity.enums.MeetingStatus +import org.springframework.test.util.ReflectionTestUtils import java.time.LocalDateTime object ScheduleTestFixture { - fun createEvent(): Event = - Event - .builder() - .title("Test Meeting") - .location("Test Location") - .start(LocalDateTime.now()) - .end(LocalDateTime.now().plusDays(2)) - .cardinal(1) - .build() + fun createEvent( + id: Long = 0L, + title: String = "Test Event", + content: String = "Test Content", + location: String = "Test Location", + cardinal: Int = 1, + start: LocalDateTime = LocalDateTime.of(2026, 3, 1, 10, 0), + end: LocalDateTime = LocalDateTime.of(2026, 3, 1, 12, 0), + ): Event { + val event = + Event + .builder() + .title(title) + .content(content) + .location(location) + .cardinal(cardinal) + .start(start) + .end(end) + .build() + if (id != 0L) ReflectionTestUtils.setField(event, "id", id) + return event + } - fun createMeeting(): Meeting = - Meeting - .builder() - .title("Test Meeting") - .location("Test Location") - .start(LocalDateTime.now()) - .end(LocalDateTime.now().plusDays(2)) - .code(1234) - .cardinal(1) - .build() + fun createMeeting( + id: Long = 0L, + title: String = "Test Meeting", + content: String = "Test Content", + location: String = "Test Location", + cardinal: Int = 1, + code: Int = 1234, + meetingStatus: MeetingStatus = MeetingStatus.OPEN, + start: LocalDateTime = LocalDateTime.of(2026, 3, 1, 10, 0), + end: LocalDateTime = LocalDateTime.of(2026, 3, 1, 12, 0), + ): Meeting { + val meeting = + Meeting + .builder() + .title(title) + .content(content) + .location(location) + .cardinal(cardinal) + .code(code) + .meetingStatus(meetingStatus) + .start(start) + .end(end) + .build() + if (id != 0L) ReflectionTestUtils.setField(meeting, "id", id) + return meeting + } } From 414823639757e4fef245570c62576b020c6b45c4 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 00:48:02 +0900 Subject: [PATCH 02/62] =?UTF-8?q?refactor:=20Schedule=20entity=20event=20j?= =?UTF-8?q?ava=20->=20kotlin=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/weeth/domain/schedule/domain/entity/Event.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/{java/com/weeth/domain/schedule/domain/entity/Event.java => kotlin/com/weeth/domain/schedule/domain/entity/Event.kt} (100%) diff --git a/src/main/java/com/weeth/domain/schedule/domain/entity/Event.java b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt similarity index 100% rename from src/main/java/com/weeth/domain/schedule/domain/entity/Event.java rename to src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt From 89e123967efd63797a52dd11d43f26c601521463 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 00:49:28 +0900 Subject: [PATCH 03/62] =?UTF-8?q?refactor:=20Schedule=20entity=20event=20?= =?UTF-8?q?=EB=8F=85=EB=A6=BD=20entity=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20kotlin=20=EB=AC=B8=EB=B2=95=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/schedule/domain/entity/Event.kt | 92 ++++++++++++++++--- 1 file changed, 78 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt index b9b5253f..6b3ae6fc 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt @@ -1,20 +1,84 @@ -package com.weeth.domain.schedule.domain.entity; +package com.weeth.domain.schedule.domain.entity -import jakarta.persistence.Entity; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; -import com.weeth.domain.user.domain.entity.User; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; +import com.weeth.domain.user.domain.entity.User +import com.weeth.global.common.entity.BaseEntity +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import java.time.LocalDateTime @Entity -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@SuperBuilder -public class Event extends Schedule { +class Event( + var title: String, - public void update(ScheduleDTO.Update dto, User user) { - this.updateUpperClass(dto, user); + @Column(columnDefinition = "TEXT") + var content: String, + + var location: String, + + var cardinal: Int, + + var requiredItem: String? = null, + + var start: LocalDateTime, + + var end: LocalDateTime, + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + var user: User? = null, +) : BaseEntity() { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long = 0 + + fun update( + title: String, + content: String, + location: String, + requiredItem: String?, + start: LocalDateTime, + end: LocalDateTime, + user: User?, + ) { + this.title = title + this.content = content + this.location = location + this.requiredItem = requiredItem + this.start = start + this.end = end + this.user = user + } + + companion object { + fun create( + title: String, + content: String, + location: String, + cardinal: Int, + requiredItem: String?, + start: LocalDateTime, + end: LocalDateTime, + user: User?, + ): Event { + require(title.isNotBlank()) { "제목은 필수입니다" } + require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" } + return Event( + title = title, + content = content, + location = location, + cardinal = cardinal, + requiredItem = requiredItem, + start = start, + end = end, + user = user, + ) + } } } From 807a9f705ce656250908d80a63258c517dd73753 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 00:59:34 +0900 Subject: [PATCH 04/62] =?UTF-8?q?refactor:=20Schedule=20entity=20event?= =?UTF-8?q?=EA=B3=BC=20=EA=B4=80=EB=A0=A8=EB=90=9C=20=EC=B0=B8=EC=A1=B0=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/mapper/EventMapper.java | 1 + .../application/mapper/ScheduleMapper.java | 3 +++ .../domain/service/EventUpdateService.java | 2 +- .../domain/schedule/domain/entity/Event.kt | 8 ------- .../usecase/MeetingUseCaseImplTest.kt | 24 ++++++++++++++----- .../schedule/fixture/ScheduleTestFixture.kt | 19 ++++++++------- 6 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/weeth/domain/schedule/application/mapper/EventMapper.java b/src/main/java/com/weeth/domain/schedule/application/mapper/EventMapper.java index c297891c..e3c18272 100644 --- a/src/main/java/com/weeth/domain/schedule/application/mapper/EventMapper.java +++ b/src/main/java/com/weeth/domain/schedule/application/mapper/EventMapper.java @@ -14,6 +14,7 @@ public interface EventMapper { @Mapping(target = "type", expression = "java(Type.EVENT)") Response to(Event event); + @BeanMapping(builder = @Builder(disableBuilder = true)) @Mappings({ @Mapping(target = "id", ignore = true), @Mapping(target = "user", source = "user") diff --git a/src/main/java/com/weeth/domain/schedule/application/mapper/ScheduleMapper.java b/src/main/java/com/weeth/domain/schedule/application/mapper/ScheduleMapper.java index c61299bd..e4b953ac 100644 --- a/src/main/java/com/weeth/domain/schedule/application/mapper/ScheduleMapper.java +++ b/src/main/java/com/weeth/domain/schedule/application/mapper/ScheduleMapper.java @@ -1,6 +1,7 @@ package com.weeth.domain.schedule.application.mapper; import com.weeth.domain.schedule.application.dto.ScheduleDTO; +import com.weeth.domain.schedule.domain.entity.Event; import com.weeth.domain.schedule.domain.entity.Schedule; import org.mapstruct.Mapper; import org.mapstruct.MappingConstants; @@ -10,4 +11,6 @@ public interface ScheduleMapper { ScheduleDTO.Response toScheduleDTO(Schedule schedule, Boolean isMeeting); + + ScheduleDTO.Response toScheduleDTO(Event event, Boolean isMeeting); } diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java b/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java index 905af593..4ff206e8 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java +++ b/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java @@ -13,6 +13,6 @@ public class EventUpdateService { public void update(Event event, ScheduleDTO.Update dto, User user) { - event.update(dto, user); + event.update(dto.title(), dto.content(), dto.location(), dto.requiredItem(), dto.start(), dto.end(), user); } } diff --git a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt index 6b3ae6fc..83394b53 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt @@ -15,25 +15,17 @@ import java.time.LocalDateTime @Entity class Event( var title: String, - @Column(columnDefinition = "TEXT") var content: String, - var location: String, - var cardinal: Int, - var requiredItem: String? = null, - var start: LocalDateTime, - var end: LocalDateTime, - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") var user: User? = null, ) : BaseEntity() { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long = 0 diff --git a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt index 3906ad75..d5b2ac79 100644 --- a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt +++ b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt @@ -68,10 +68,18 @@ class MeetingUseCaseImplTest : beforeTest { clearMocks( - meetingGetService, meetingMapper, meetingSaveService, userGetService, - meetingUpdateService, meetingDeleteService, attendanceGetService, - attendanceSaveService, attendanceDeleteService, attendanceUpdateService, - cardinalGetService, em, + meetingGetService, + meetingMapper, + meetingSaveService, + userGetService, + meetingUpdateService, + meetingDeleteService, + attendanceGetService, + attendanceSaveService, + attendanceDeleteService, + attendanceUpdateService, + cardinalGetService, + em, ) } @@ -153,8 +161,12 @@ class MeetingUseCaseImplTest : val meeting = ScheduleTestFixture.createMeeting() val dto = ScheduleDTO.Save( - "Title", "Content", "Location", null, - Type.MEETING, 1, + "Title", + "Content", + "Location", + null, + Type.MEETING, + 1, LocalDateTime.of(2026, 3, 1, 10, 0), LocalDateTime.of(2026, 3, 1, 12, 0), ) diff --git a/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt b/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt index 8adc4a60..c86f7ae2 100644 --- a/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt +++ b/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt @@ -17,15 +17,16 @@ object ScheduleTestFixture { end: LocalDateTime = LocalDateTime.of(2026, 3, 1, 12, 0), ): Event { val event = - Event - .builder() - .title(title) - .content(content) - .location(location) - .cardinal(cardinal) - .start(start) - .end(end) - .build() + Event.create( + title = title, + content = content, + location = location, + cardinal = cardinal, + requiredItem = null, + start = start, + end = end, + user = null, + ) if (id != 0L) ReflectionTestUtils.setField(event, "id", id) return event } From 826654e457d523480ba2c3d0e00a564e9665d96f Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 01:41:37 +0900 Subject: [PATCH 05/62] =?UTF-8?q?refactor:=20Meeting=20=E2=86=92=20Session?= =?UTF-8?q?,=20Attendance=20kotlin=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=20=EB=B0=8F=20attendance=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../schedule/domain/entity/enums/Type.java | 2 +- .../attendance/domain/entity/Attendance.kt | 56 +++++++++++ .../attendance/domain/entity/Session.kt | 96 +++++++++++++++++++ .../domain/entity/enums/AttendanceStatus.kt | 7 ++ .../domain/entity/enums/SessionStatus.kt | 6 ++ .../domain/repository/SessionRepository.kt | 26 +++++ 6 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt create mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt create mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/AttendanceStatus.kt create mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/SessionStatus.kt create mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt diff --git a/src/main/java/com/weeth/domain/schedule/domain/entity/enums/Type.java b/src/main/java/com/weeth/domain/schedule/domain/entity/enums/Type.java index ca0c721d..0179105b 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/entity/enums/Type.java +++ b/src/main/java/com/weeth/domain/schedule/domain/entity/enums/Type.java @@ -1,4 +1,4 @@ -package com.weeth.domain.schedule.domain.entity.enums; +2package com.weeth.domain.schedule.domain.entity.enums; public enum Type { EVENT, MEETING diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt new file mode 100644 index 00000000..efc910f1 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt @@ -0,0 +1,56 @@ +package com.weeth.domain.attendance.domain.entity + +import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus +import com.weeth.domain.user.domain.entity.User +import com.weeth.global.common.entity.BaseEntity +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne + +@Entity +class Attendance( + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "meeting_id") + val session: Session, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + val user: User, + @Enumerated(EnumType.STRING) + var status: AttendanceStatus = AttendanceStatus.PENDING, +) : BaseEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "attendance_id") + val id: Long = 0 + + fun attend() { + check(status == AttendanceStatus.PENDING) { "이미 처리된 출석입니다" } + status = AttendanceStatus.ATTEND + } + + fun absent() { + check(status == AttendanceStatus.PENDING) { "이미 처리된 출석입니다" } + status = AttendanceStatus.ABSENT + } + + // 기존 close() 는 absent() 로 대체 (AttendanceUpdateService 호환 유지) + fun close() = absent() + + fun isPending(): Boolean = status == AttendanceStatus.PENDING + + fun isWrong(code: Int): Boolean = !session.isCodeMatch(code) + + companion object { + fun create( + session: Session, + user: User, + ): Attendance = Attendance(session = session, user = user) + } +} diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt new file mode 100644 index 00000000..8d421540 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt @@ -0,0 +1,96 @@ +package com.weeth.domain.attendance.domain.entity + +import com.weeth.domain.attendance.domain.entity.enums.SessionStatus +import com.weeth.domain.user.domain.entity.User +import com.weeth.global.common.entity.BaseEntity +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import java.time.LocalDateTime + +@Entity +@Table(name = "meeting") +class Session( + var title: String, + @Column(columnDefinition = "TEXT") + var content: String? = null, + var location: String? = null, + var cardinal: Int, + var requiredItem: String? = null, + var start: LocalDateTime, + var end: LocalDateTime, + var code: Int, + @Enumerated(EnumType.STRING) + var status: SessionStatus = SessionStatus.OPEN, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + var user: User? = null, +) : BaseEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long = 0 + + fun close() { + check(status == SessionStatus.OPEN) { "이미 종료된 세션입니다" } + status = SessionStatus.CLOSED + } + + fun updateInfo( + title: String, + content: String?, + location: String?, + requiredItem: String?, + start: LocalDateTime, + end: LocalDateTime, + user: User?, + ) { + this.title = title + this.content = content + this.location = location + this.requiredItem = requiredItem + this.start = start + this.end = end + this.user = user + } + + fun isCodeMatch(code: Int): Boolean = this.code == code + + fun isInProgress(now: LocalDateTime): Boolean = !now.isBefore(start) && !now.isAfter(end) + + companion object { + fun create( + title: String, + content: String?, + location: String?, + cardinal: Int, + requiredItem: String?, + start: LocalDateTime, + end: LocalDateTime, + user: User?, + ): Session { + require(title.isNotBlank()) { "제목은 필수입니다" } + require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" } + return Session( + title = title, + content = content, + location = location, + cardinal = cardinal, + requiredItem = requiredItem, + start = start, + end = end, + code = generateCode(), + user = user, + ) + } + + private fun generateCode(): Int = (1000..9999).random() + } +} diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/AttendanceStatus.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/AttendanceStatus.kt new file mode 100644 index 00000000..f54fc9f0 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/AttendanceStatus.kt @@ -0,0 +1,7 @@ +package com.weeth.domain.attendance.domain.entity.enums + +enum class AttendanceStatus { + ATTEND, + PENDING, + ABSENT, +} diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/SessionStatus.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/SessionStatus.kt new file mode 100644 index 00000000..ee4da296 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/SessionStatus.kt @@ -0,0 +1,6 @@ +package com.weeth.domain.attendance.domain.entity.enums + +enum class SessionStatus { + OPEN, + CLOSED, +} diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt new file mode 100644 index 00000000..e6ba28a7 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt @@ -0,0 +1,26 @@ +package com.weeth.domain.attendance.domain.repository + +import com.weeth.domain.attendance.domain.entity.Session +import com.weeth.domain.attendance.domain.entity.enums.SessionStatus +import org.springframework.data.jpa.repository.JpaRepository +import java.time.LocalDateTime + +interface SessionRepository : JpaRepository { + fun findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc( + end: LocalDateTime, + start: LocalDateTime, + ): List + + fun findAllByCardinalOrderByStartAsc(cardinal: Int): List + + fun findAllByCardinalOrderByStartDesc(cardinal: Int): List + + fun findAllByCardinal(cardinal: Int): List + + fun findAllByStatusAndEndBeforeOrderByEndAsc( + status: SessionStatus, + end: LocalDateTime, + ): List + + fun findAllByOrderByStartDesc(): List +} From 22d739b5364575910b813d8d098782fb33ad13d1 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 01:41:54 +0900 Subject: [PATCH 06/62] =?UTF-8?q?refactor:=20Attendance=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=B0=B8=EC=A1=B0=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?Session/AttendanceStatus=EB=A1=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/AttendanceDTO.java | 8 +-- .../application/mapper/AttendanceMapper.java | 26 ++++---- .../usecase/AttendanceUseCaseImpl.java | 43 ++++++------- .../attendance/domain/entity/Attendance.java | 62 ------------------- .../domain/entity/enums/Status.java | 7 --- .../repository/AttendanceRepository.java | 8 +-- .../service/AttendanceDeleteService.java | 6 +- .../domain/service/AttendanceGetService.java | 7 ++- .../domain/service/AttendanceSaveService.java | 14 ++--- .../domain/service/AttendanceScheduler.java | 14 ++--- .../service/AttendanceUpdateService.java | 4 +- 11 files changed, 64 insertions(+), 135 deletions(-) delete mode 100644 src/main/java/com/weeth/domain/attendance/domain/entity/Attendance.java delete mode 100644 src/main/java/com/weeth/domain/attendance/domain/entity/enums/Status.java diff --git a/src/main/java/com/weeth/domain/attendance/application/dto/AttendanceDTO.java b/src/main/java/com/weeth/domain/attendance/application/dto/AttendanceDTO.java index 072ea499..1c7d1f5c 100644 --- a/src/main/java/com/weeth/domain/attendance/application/dto/AttendanceDTO.java +++ b/src/main/java/com/weeth/domain/attendance/application/dto/AttendanceDTO.java @@ -3,7 +3,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; -import com.weeth.domain.attendance.domain.entity.enums.Status; +import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus; import java.time.LocalDateTime; import java.util.List; @@ -13,7 +13,7 @@ public class AttendanceDTO { public record Main( Integer attendanceRate, String title, - Status status, + AttendanceStatus status, @Schema(description = "어드민인 경우 출석 코드 노출") Integer code, LocalDateTime start, @@ -30,7 +30,7 @@ public record Detail( public record Response( Long id, - Status status, + AttendanceStatus status, String title, LocalDateTime start, LocalDateTime end, @@ -43,7 +43,7 @@ public record CheckIn( public record AttendanceInfo( Long id, - Status status, + AttendanceStatus status, String name, String position, String department, diff --git a/src/main/java/com/weeth/domain/attendance/application/mapper/AttendanceMapper.java b/src/main/java/com/weeth/domain/attendance/application/mapper/AttendanceMapper.java index 2b592858..14264f91 100644 --- a/src/main/java/com/weeth/domain/attendance/application/mapper/AttendanceMapper.java +++ b/src/main/java/com/weeth/domain/attendance/application/mapper/AttendanceMapper.java @@ -12,23 +12,23 @@ public interface AttendanceMapper { @Mappings({ @Mapping(target = "attendanceRate", source = "user.attendanceRate"), - @Mapping(target = "title", source = "attendance.meeting.title"), + @Mapping(target = "title", source = "attendance.session.title"), @Mapping(target = "status", source = "attendance.status"), @Mapping(target = "code", ignore = true), - @Mapping(target = "start", source = "attendance.meeting.start"), - @Mapping(target = "end", source = "attendance.meeting.end"), - @Mapping(target = "location", source = "attendance.meeting.location"), + @Mapping(target = "start", source = "attendance.session.start"), + @Mapping(target = "end", source = "attendance.session.end"), + @Mapping(target = "location", source = "attendance.session.location"), }) AttendanceDTO.Main toMainDto(User user, Attendance attendance); @Mappings({ @Mapping(target = "attendanceRate", source = "user.attendanceRate"), - @Mapping(target = "title", source = "attendance.meeting.title"), + @Mapping(target = "title", source = "attendance.session.title"), @Mapping(target = "status", source = "attendance.status"), - @Mapping(target = "code", source = "attendance.meeting.code"), - @Mapping(target = "start", source = "attendance.meeting.start"), - @Mapping(target = "end", source = "attendance.meeting.end"), - @Mapping(target = "location", source = "attendance.meeting.location"), + @Mapping(target = "code", source = "attendance.session.code"), + @Mapping(target = "start", source = "attendance.session.start"), + @Mapping(target = "end", source = "attendance.session.end"), + @Mapping(target = "location", source = "attendance.session.location"), }) AttendanceDTO.Main toAdminResponse(User user, Attendance attendance); @@ -39,10 +39,10 @@ public interface AttendanceMapper { AttendanceDTO.Detail toDetailDto(User user, List attendances); @Mappings({ - @Mapping(target = "title", source = "attendance.meeting.title"), - @Mapping(target = "start", source = "attendance.meeting.start"), - @Mapping(target = "end", source = "attendance.meeting.end"), - @Mapping(target = "location", source = "attendance.meeting.location"), + @Mapping(target = "title", source = "attendance.session.title"), + @Mapping(target = "start", source = "attendance.session.start"), + @Mapping(target = "end", source = "attendance.session.end"), + @Mapping(target = "location", source = "attendance.session.location"), }) AttendanceDTO.Response toResponseDto(Attendance attendance); @Mappings({ diff --git a/src/main/java/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImpl.java b/src/main/java/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImpl.java index 50c71581..82618dcc 100644 --- a/src/main/java/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImpl.java +++ b/src/main/java/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImpl.java @@ -5,11 +5,11 @@ import com.weeth.domain.attendance.application.exception.AttendanceNotFoundException; import com.weeth.domain.attendance.application.mapper.AttendanceMapper; import com.weeth.domain.attendance.domain.entity.Attendance; -import com.weeth.domain.attendance.domain.entity.enums.Status; +import com.weeth.domain.attendance.domain.entity.Session; +import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus; import com.weeth.domain.attendance.domain.service.AttendanceGetService; import com.weeth.domain.attendance.domain.service.AttendanceUpdateService; import com.weeth.domain.schedule.application.exception.MeetingNotFoundException; -import com.weeth.domain.schedule.domain.entity.Meeting; import com.weeth.domain.schedule.domain.service.MeetingGetService; import com.weeth.domain.user.domain.entity.Cardinal; import com.weeth.domain.user.domain.entity.User; @@ -45,15 +45,15 @@ public void checkIn(Long userId, Integer code) throws AttendanceCodeMismatchExce LocalDateTime now = LocalDateTime.now(); Attendance todayMeeting = user.getAttendances().stream() - .filter(attendance -> attendance.getMeeting().getStart().minusMinutes(10).isBefore(now) - && attendance.getMeeting().getEnd().isAfter(now)) + .filter(attendance -> attendance.getSession().getStart().minusMinutes(10).isBefore(now) + && attendance.getSession().getEnd().isAfter(now)) .findAny() .orElseThrow(AttendanceNotFoundException::new); if (todayMeeting.isWrong(code)) throw new AttendanceCodeMismatchException(); - if (todayMeeting.getStatus() != Status.ATTEND) + if (todayMeeting.getStatus() != AttendanceStatus.ATTEND) attendanceUpdateService.attend(todayMeeting); } @@ -62,8 +62,8 @@ public AttendanceDTO.Main find(Long userId) { User user = userGetService.find(userId); Attendance todayMeeting = user.getAttendances().stream() - .filter(attendance -> attendance.getMeeting().getStart().toLocalDate().isEqual(LocalDate.now()) - && attendance.getMeeting().getEnd().toLocalDate().isEqual(LocalDate.now())) + .filter(attendance -> attendance.getSession().getStart().toLocalDate().isEqual(LocalDate.now()) + && attendance.getSession().getEnd().toLocalDate().isEqual(LocalDate.now())) .findAny() .orElse(null); @@ -79,8 +79,8 @@ public AttendanceDTO.Detail findAllDetailsByCurrentCardinal(Long userId) { Cardinal currentCardinal = userCardinalGetService.getCurrentCardinal(user); List responses = user.getAttendances().stream() - .filter(attendance -> attendance.getMeeting().getCardinal().equals(currentCardinal.getCardinalNumber())) - .sorted(Comparator.comparing(attendance -> attendance.getMeeting().getStart())) + .filter(attendance -> attendance.getSession().getCardinal() == currentCardinal.getCardinalNumber()) + .sorted(Comparator.comparing(attendance -> attendance.getSession().getStart())) .map(mapper::toResponseDto) .toList(); @@ -88,10 +88,10 @@ public AttendanceDTO.Detail findAllDetailsByCurrentCardinal(Long userId) { } @Override - public List findAllAttendanceByMeeting(Long meetingId) { - Meeting meeting = meetingGetService.find(meetingId); + public List findAllAttendanceByMeeting(Long sessionId) { + Session session = meetingGetService.find(sessionId); - List attendances = attendanceGetService.findAllByMeeting(meeting); + List attendances = attendanceGetService.findAllByMeeting(session); return attendances.stream() .map(mapper::toAttendanceInfoDto) @@ -100,18 +100,15 @@ public List findAllAttendanceByMeeting(Long meetin @Override public void close(LocalDate now, Integer cardinal) { - List meetings = meetingGetService.find(cardinal); - - /* - todo 차후 리팩토링 정기모임 id를 입력받아서 해당 정기모임의 출석을 마감하도록 수정 - */ - Meeting targetMeeting = meetings.stream() - .filter(meeting -> meeting.getStart().toLocalDate().isEqual(now) - && meeting.getEnd().toLocalDate().isEqual(now)) + List sessions = meetingGetService.find(cardinal); + + Session targetSession = sessions.stream() + .filter(session -> session.getStart().toLocalDate().isEqual(now) + && session.getEnd().toLocalDate().isEqual(now)) .findAny() .orElseThrow(MeetingNotFoundException::new); - List attendanceList = attendanceGetService.findAllByMeeting(targetMeeting); + List attendanceList = attendanceGetService.findAllByMeeting(targetSession); attendanceUpdateService.close(attendanceList); } @@ -123,9 +120,9 @@ public void updateAttendanceStatus(List attendanceUp Attendance attendance = attendanceGetService.findByAttendanceId(update.attendanceId()); User user = attendance.getUser(); - Status newStatus = Status.valueOf(update.status()); + AttendanceStatus newStatus = AttendanceStatus.valueOf(update.status()); - if (newStatus == Status.ABSENT) { + if (newStatus == AttendanceStatus.ABSENT) { attendance.close(); user.removeAttend(); user.absent(); diff --git a/src/main/java/com/weeth/domain/attendance/domain/entity/Attendance.java b/src/main/java/com/weeth/domain/attendance/domain/entity/Attendance.java deleted file mode 100644 index 35197ad3..00000000 --- a/src/main/java/com/weeth/domain/attendance/domain/entity/Attendance.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.weeth.domain.attendance.domain.entity; - -import jakarta.persistence.*; -import com.weeth.domain.attendance.domain.entity.enums.Status; -import com.weeth.domain.schedule.domain.entity.Meeting; -import com.weeth.domain.user.domain.entity.User; -import com.weeth.global.common.entity.BaseEntity; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; - -@Entity -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor -@SuperBuilder -public class Attendance extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "attendance_id") - private Long id; - - @Enumerated(EnumType.STRING) - private Status status; - - @ManyToOne - @JoinColumn(name = "meeting_id") - private Meeting meeting; - - @ManyToOne - @JoinColumn(name = "user_id") - private User user; - - @PrePersist - public void init() { - this.status = Status.PENDING; - } - - public Attendance(Meeting meeting, User user) { - this.meeting = meeting; - this.user = user; - } - - public void attend() { - this.status = Status.ATTEND; - } - - public void close() { - this.status = Status.ABSENT; - } - - public boolean isPending() { - return this.status == Status.PENDING; - } - - public boolean isWrong(Integer code) { - return !this.meeting.getCode().equals(code); - } -} diff --git a/src/main/java/com/weeth/domain/attendance/domain/entity/enums/Status.java b/src/main/java/com/weeth/domain/attendance/domain/entity/enums/Status.java deleted file mode 100644 index 4dbd7466..00000000 --- a/src/main/java/com/weeth/domain/attendance/domain/entity/enums/Status.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.weeth.domain.attendance.domain.entity.enums; - -public enum Status { - ATTEND, - PENDING, - ABSENT -} diff --git a/src/main/java/com/weeth/domain/attendance/domain/repository/AttendanceRepository.java b/src/main/java/com/weeth/domain/attendance/domain/repository/AttendanceRepository.java index 6fe213fa..df7a84b1 100644 --- a/src/main/java/com/weeth/domain/attendance/domain/repository/AttendanceRepository.java +++ b/src/main/java/com/weeth/domain/attendance/domain/repository/AttendanceRepository.java @@ -1,7 +1,7 @@ package com.weeth.domain.attendance.domain.repository; import com.weeth.domain.attendance.domain.entity.Attendance; -import com.weeth.domain.schedule.domain.entity.Meeting; +import com.weeth.domain.attendance.domain.entity.Session; import com.weeth.domain.user.domain.entity.enums.Status; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; @@ -10,9 +10,9 @@ import java.util.List; public interface AttendanceRepository extends JpaRepository { - List findAllByMeetingAndUserStatus(Meeting meeting, Status status); + List findAllBySessionAndUserStatus(Session session, Status status); @Modifying - @Query("DELETE FROM Attendance a WHERE a.meeting = :meeting") - void deleteAllByMeeting(Meeting meeting); + @Query("DELETE FROM Attendance a WHERE a.session = :session") + void deleteAllBySession(Session session); } diff --git a/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceDeleteService.java b/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceDeleteService.java index 853c21ab..4eb2fad0 100644 --- a/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceDeleteService.java +++ b/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceDeleteService.java @@ -1,8 +1,8 @@ package com.weeth.domain.attendance.domain.service; import com.weeth.domain.attendance.domain.entity.Attendance; +import com.weeth.domain.attendance.domain.entity.Session; import com.weeth.domain.attendance.domain.repository.AttendanceRepository; -import com.weeth.domain.schedule.domain.entity.Meeting; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -14,7 +14,7 @@ public class AttendanceDeleteService { private final AttendanceRepository attendanceRepository; - public void deleteAll(Meeting meeting) { - attendanceRepository.deleteAllByMeeting(meeting); + public void deleteAll(Session session) { + attendanceRepository.deleteAllBySession(session); } } diff --git a/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceGetService.java b/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceGetService.java index 82c6d8c1..c6b996db 100644 --- a/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceGetService.java +++ b/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceGetService.java @@ -2,8 +2,8 @@ import com.weeth.domain.attendance.application.exception.AttendanceNotFoundException; import com.weeth.domain.attendance.domain.entity.Attendance; +import com.weeth.domain.attendance.domain.entity.Session; import com.weeth.domain.attendance.domain.repository.AttendanceRepository; -import com.weeth.domain.schedule.domain.entity.Meeting; import com.weeth.domain.user.domain.entity.enums.Status; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -16,9 +16,10 @@ public class AttendanceGetService { private final AttendanceRepository attendanceRepository; - public List findAllByMeeting(Meeting meeting) { - return attendanceRepository.findAllByMeetingAndUserStatus(meeting, Status.ACTIVE); + public List findAllByMeeting(Session session) { + return attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE); } + public Attendance findByAttendanceId(Long attendanceId) { return attendanceRepository.findById(attendanceId) .orElseThrow(AttendanceNotFoundException::new); diff --git a/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceSaveService.java b/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceSaveService.java index 8ad82e6b..afb56623 100644 --- a/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceSaveService.java +++ b/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceSaveService.java @@ -2,8 +2,8 @@ import jakarta.transaction.Transactional; import com.weeth.domain.attendance.domain.entity.Attendance; +import com.weeth.domain.attendance.domain.entity.Session; import com.weeth.domain.attendance.domain.repository.AttendanceRepository; -import com.weeth.domain.schedule.domain.entity.Meeting; import com.weeth.domain.user.domain.entity.User; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -17,18 +17,18 @@ public class AttendanceSaveService { private final AttendanceRepository attendanceRepository; - public void init(User user, List meetings) { - if (meetings != null) { - meetings.forEach(meeting -> { - Attendance attendance = attendanceRepository.save(new Attendance(meeting, user)); + public void init(User user, List sessions) { + if (sessions != null) { + sessions.forEach(session -> { + Attendance attendance = attendanceRepository.save(Attendance.Companion.create(session, user)); user.add(attendance); }); } } - public void saveAll(List userList, Meeting meeting) { + public void saveAll(List userList, Session session) { List attendances = userList.stream() - .map(user -> new Attendance(meeting, user)) + .map(user -> Attendance.Companion.create(session, user)) .toList(); attendanceRepository.saveAll(attendances); diff --git a/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceScheduler.java b/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceScheduler.java index 57b541e3..567caeb5 100644 --- a/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceScheduler.java +++ b/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceScheduler.java @@ -3,7 +3,7 @@ import jakarta.transaction.Transactional; import java.util.List; import com.weeth.domain.attendance.domain.entity.Attendance; -import com.weeth.domain.schedule.domain.entity.Meeting; +import com.weeth.domain.attendance.domain.entity.Session; import com.weeth.domain.schedule.domain.service.MeetingGetService; import lombok.RequiredArgsConstructor; import org.springframework.scheduling.annotation.Scheduled; @@ -20,12 +20,12 @@ public class AttendanceScheduler { @Transactional @Scheduled(cron = "0 0 22 * * THU", zone = "Asia/Seoul") public void autoCloseAttendance() { - List meetings = meetingGetService.findAllOpenMeetingsBeforeNow(); + List sessions = meetingGetService.findAllOpenMeetingsBeforeNow(); - meetings.forEach(meeting -> { - meeting.close(); - List attendanceList = attendanceGetService.findAllByMeeting(meeting); - attendanceUpdateService.close(attendanceList); - }); + sessions.forEach(session -> { + session.close(); + List attendanceList = attendanceGetService.findAllByMeeting(session); + attendanceUpdateService.close(attendanceList); + }); } } diff --git a/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceUpdateService.java b/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceUpdateService.java index 4bfbeb6b..84926b6f 100644 --- a/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceUpdateService.java +++ b/src/main/java/com/weeth/domain/attendance/domain/service/AttendanceUpdateService.java @@ -2,7 +2,7 @@ import jakarta.transaction.Transactional; import com.weeth.domain.attendance.domain.entity.Attendance; -import com.weeth.domain.attendance.domain.entity.enums.Status; +import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus; import com.weeth.domain.user.domain.entity.User; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -31,7 +31,7 @@ public void close(List attendances) { public void updateUserAttendanceByStatus(List attendances) { for (Attendance attendance : attendances) { User user = attendance.getUser(); - if (attendance.getStatus().equals(Status.ATTEND)) { + if (attendance.getStatus().equals(AttendanceStatus.ATTEND)) { user.removeAttend(); } else { user.removeAbsent(); From d4f9d71faccb54bf043b99d6b6548cade9ed70cd Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 01:42:22 +0900 Subject: [PATCH 07/62] =?UTF-8?q?refactor:=20Schedule=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=B0=B8=EC=A1=B0=20=ED=8C=8C=EC=9D=BC=20Session?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/mapper/MeetingMapper.java | 24 +++----- .../application/mapper/ScheduleMapper.java | 6 +- .../usecase/MeetingUseCaseImpl.java | 58 +++++++++---------- .../schedule/domain/entity/Meeting.java | 40 ------------- .../schedule/domain/entity/Schedule.java | 55 ------------------ .../domain/entity/enums/MeetingStatus.java | 6 -- .../domain/repository/MeetingRepository.java | 23 -------- .../domain/service/MeetingDeleteService.java | 10 ++-- .../domain/service/MeetingGetService.java | 36 ++++++------ .../domain/service/MeetingSaveService.java | 10 ++-- .../domain/service/MeetingUpdateService.java | 6 +- 11 files changed, 69 insertions(+), 205 deletions(-) delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/entity/Meeting.java delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/entity/Schedule.java delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/entity/enums/MeetingStatus.java delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/repository/MeetingRepository.java diff --git a/src/main/java/com/weeth/domain/schedule/application/mapper/MeetingMapper.java b/src/main/java/com/weeth/domain/schedule/application/mapper/MeetingMapper.java index c81c72bc..d7741c73 100644 --- a/src/main/java/com/weeth/domain/schedule/application/mapper/MeetingMapper.java +++ b/src/main/java/com/weeth/domain/schedule/application/mapper/MeetingMapper.java @@ -1,7 +1,7 @@ package com.weeth.domain.schedule.application.mapper; +import com.weeth.domain.attendance.domain.entity.Session; import com.weeth.domain.schedule.application.dto.ScheduleDTO; -import com.weeth.domain.schedule.domain.entity.Meeting; import com.weeth.domain.user.domain.entity.User; import org.mapstruct.*; @@ -16,34 +16,24 @@ public interface MeetingMapper { @Mapping(target = "name", source = "user.name") @Mapping(target = "code", ignore = true) @Mapping(target = "type", expression = "java(Type.MEETING)") - Response to(Meeting meeting); + Response to(Session session); - Info toInfo(Meeting meeting); + Info toInfo(Session session); @Mapping(target = "name", source = "user.name") @Mapping(target = "type", expression = "java(Type.MEETING)") - Response toAdminResponse(Meeting meeting); + Response toAdminResponse(Session session); + @BeanMapping(builder = @Builder(disableBuilder = true)) @Mappings({ @Mapping(target = "id", ignore = true), @Mapping(target = "code", expression = "java( generateCode() )"), + @Mapping(target = "status", ignore = true), @Mapping(target = "user", source = "user") }) - Meeting from(ScheduleDTO.Save dto, User user); + Session from(ScheduleDTO.Save dto, User user); default Integer generateCode() { return new Random().nextInt(9000) + 1000; } - - /* - 차후 필히 리팩토링 할 것 - -> 정기 모임의 참여하는 인원의 멤버수를 어떻게 관리할지. - 해당 코드는 일시적인 대안책임 - */ -// default Integer getMemberCount(Meeting meeting) { -// return (int)meeting.getAttendances().stream() -// .filter(attendance -> !attendance.getUser().getStatus().equals(Status.BANNED)) -// .filter(attendance -> !attendance.getUser().getStatus().equals(Status.LEFT)) -// .count(); -// } } diff --git a/src/main/java/com/weeth/domain/schedule/application/mapper/ScheduleMapper.java b/src/main/java/com/weeth/domain/schedule/application/mapper/ScheduleMapper.java index e4b953ac..e316c216 100644 --- a/src/main/java/com/weeth/domain/schedule/application/mapper/ScheduleMapper.java +++ b/src/main/java/com/weeth/domain/schedule/application/mapper/ScheduleMapper.java @@ -1,8 +1,8 @@ package com.weeth.domain.schedule.application.mapper; +import com.weeth.domain.attendance.domain.entity.Session; import com.weeth.domain.schedule.application.dto.ScheduleDTO; import com.weeth.domain.schedule.domain.entity.Event; -import com.weeth.domain.schedule.domain.entity.Schedule; import org.mapstruct.Mapper; import org.mapstruct.MappingConstants; import org.mapstruct.ReportingPolicy; @@ -10,7 +10,7 @@ @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface ScheduleMapper { - ScheduleDTO.Response toScheduleDTO(Schedule schedule, Boolean isMeeting); - ScheduleDTO.Response toScheduleDTO(Event event, Boolean isMeeting); + + ScheduleDTO.Response toScheduleDTO(Session session, Boolean isMeeting); } diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java b/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java index 21a25151..97351d1c 100644 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java +++ b/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java @@ -3,6 +3,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import com.weeth.domain.attendance.domain.entity.Attendance; +import com.weeth.domain.attendance.domain.entity.Session; import com.weeth.domain.attendance.domain.service.AttendanceDeleteService; import com.weeth.domain.attendance.domain.service.AttendanceGetService; import com.weeth.domain.attendance.domain.service.AttendanceSaveService; @@ -10,7 +11,6 @@ import com.weeth.domain.schedule.application.dto.MeetingDTO; import com.weeth.domain.schedule.application.dto.ScheduleDTO; import com.weeth.domain.schedule.application.mapper.MeetingMapper; -import com.weeth.domain.schedule.domain.entity.Meeting; import com.weeth.domain.schedule.domain.service.MeetingDeleteService; import com.weeth.domain.schedule.domain.service.MeetingGetService; import com.weeth.domain.schedule.domain.service.MeetingSaveService; @@ -55,29 +55,29 @@ public class MeetingUseCaseImpl implements MeetingUseCase { private EntityManager em; @Override - public Response find(Long userId, Long meetingId) { + public Response find(Long userId, Long sessionId) { User user = userGetService.find(userId); - Meeting meeting = meetingGetService.find(meetingId); + Session session = meetingGetService.find(sessionId); if (Role.ADMIN == user.getRole()) { - return mapper.toAdminResponse(meeting) ; + return mapper.toAdminResponse(session); } - return mapper.to(meeting); + return mapper.to(session); } @Override public MeetingDTO.Infos find(Integer cardinal) { - List meetings; + List sessions; if (cardinal == null) { - meetings = meetingGetService.findAll(); + sessions = meetingGetService.findAll(); } else { - meetings = meetingGetService.findMeetingByCardinal(cardinal); + sessions = meetingGetService.findMeetingByCardinal(cardinal); } - Meeting thisWeek = findThisWeek(meetings); - List sorted = sortMeetings(meetings); + Session thisWeek = findThisWeek(sessions); + List sorted = sortSessions(sessions); return new MeetingDTO.Infos( thisWeek != null ? mapper.toInfo(thisWeek) : null, @@ -92,54 +92,52 @@ public void save(ScheduleDTO.Save dto, Long userId) { List userList = userGetService.findAllByCardinal(cardinal); - Meeting meeting = mapper.from(dto, user); - meetingSaveService.save(meeting); + Session session = mapper.from(dto, user); + meetingSaveService.save(session); - attendanceSaveService.saveAll(userList, meeting); + attendanceSaveService.saveAll(userList, session); } @Override @Transactional - public void update(ScheduleDTO.Update dto, Long userId, Long meetingId) { - Meeting meeting = meetingGetService.find(meetingId); + public void update(ScheduleDTO.Update dto, Long userId, Long sessionId) { + Session session = meetingGetService.find(sessionId); User user = userGetService.find(userId); - meetingUpdateService.update(dto, user, meeting); + meetingUpdateService.update(dto, user, session); } @Override @Transactional - public void delete(Long meetingId) { - Meeting meeting = meetingGetService.find(meetingId); - List attendances = attendanceGetService.findAllByMeeting(meeting); + public void delete(Long sessionId) { + Session session = meetingGetService.find(sessionId); + List attendances = attendanceGetService.findAllByMeeting(session); attendanceUpdateService.updateUserAttendanceByStatus(attendances); em.flush(); em.clear(); - attendanceDeleteService.deleteAll(meeting); - meetingDeleteService.delete(meeting); + attendanceDeleteService.deleteAll(session); + meetingDeleteService.delete(session); } - private List sortMeetings(List meetings) { - return meetings.stream() - .sorted(Comparator.comparing(Meeting::getStart).reversed()) + private List sortSessions(List sessions) { + return sessions.stream() + .sorted(Comparator.comparing(Session::getStart).reversed()) .toList(); } - - private Meeting findThisWeek(List meetings) { + private Session findThisWeek(List sessions) { LocalDate today = LocalDate.now(); LocalDate startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); LocalDate endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); - return meetings.stream() - .filter(m -> { - LocalDate d = m.getStart().toLocalDate(); + return sessions.stream() + .filter(s -> { + LocalDate d = s.getStart().toLocalDate(); return !d.isBefore(startOfWeek) && !d.isAfter(endOfWeek); }) .findFirst() .orElse(null); } - } diff --git a/src/main/java/com/weeth/domain/schedule/domain/entity/Meeting.java b/src/main/java/com/weeth/domain/schedule/domain/entity/Meeting.java deleted file mode 100644 index 30ad37fe..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/entity/Meeting.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.weeth.domain.schedule.domain.entity; - -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.PrePersist; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; -import com.weeth.domain.schedule.domain.entity.enums.MeetingStatus; -import com.weeth.domain.user.domain.entity.User; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; - -@Entity -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor -@SuperBuilder -public class Meeting extends Schedule { - - private Integer code; - - @Enumerated(EnumType.STRING) - private MeetingStatus meetingStatus; - - public void update(ScheduleDTO.Update dto, User user) { - this.updateUpperClass(dto, user); - } - - @PrePersist - public void init() { - this.meetingStatus = MeetingStatus.OPEN; - } - - public void close() { - this.meetingStatus = MeetingStatus.CLOSE; - } -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/entity/Schedule.java b/src/main/java/com/weeth/domain/schedule/domain/entity/Schedule.java deleted file mode 100644 index 3c232518..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/entity/Schedule.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.weeth.domain.schedule.domain.entity; - -import jakarta.persistence.*; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; -import com.weeth.domain.user.domain.entity.User; -import com.weeth.global.common.entity.BaseEntity; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; - -import java.time.LocalDateTime; - - -@Getter -@MappedSuperclass -@EntityListeners(AuditingEntityListener.class) -@SuperBuilder -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Schedule extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String title; - - @Column(columnDefinition = "TEXT") - private String content; - - private String location; - - private Integer cardinal; - - private String requiredItem; - - private LocalDateTime start; - - private LocalDateTime end; - - @ManyToOne - @JoinColumn(name = "user_id") - private User user; - - public void updateUpperClass(ScheduleDTO.Update dto, User user) { - this.title = dto.title(); - this.content = dto.content(); - this.location = dto.location(); - this.requiredItem = dto.requiredItem(); - this.start = dto.start(); - this.end = dto.end(); - this.user = user; - } -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/entity/enums/MeetingStatus.java b/src/main/java/com/weeth/domain/schedule/domain/entity/enums/MeetingStatus.java deleted file mode 100644 index 7ca34280..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/entity/enums/MeetingStatus.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.weeth.domain.schedule.domain.entity.enums; - -public enum MeetingStatus { - OPEN, - CLOSE -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/repository/MeetingRepository.java b/src/main/java/com/weeth/domain/schedule/domain/repository/MeetingRepository.java deleted file mode 100644 index e85d650f..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/repository/MeetingRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.weeth.domain.schedule.domain.repository; - -import com.weeth.domain.schedule.domain.entity.Meeting; -import com.weeth.domain.schedule.domain.entity.enums.MeetingStatus; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.time.LocalDateTime; -import java.util.List; - -public interface MeetingRepository extends JpaRepository { - - List findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(LocalDateTime start, LocalDateTime end); - - List findAllByCardinalOrderByStartAsc(int cardinal); - - List findAllByCardinalOrderByStartDesc(int cardinal); - - List findAllByCardinal(int cardinal); - - List findAllByMeetingStatusAndEndBeforeOrderByEndAsc(MeetingStatus status, LocalDateTime end); - - List findAllByOrderByStartDesc(); -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingDeleteService.java b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingDeleteService.java index 39fecb02..502d3120 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingDeleteService.java +++ b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingDeleteService.java @@ -1,7 +1,7 @@ package com.weeth.domain.schedule.domain.service; -import com.weeth.domain.schedule.domain.entity.Meeting; -import com.weeth.domain.schedule.domain.repository.MeetingRepository; +import com.weeth.domain.attendance.domain.entity.Session; +import com.weeth.domain.attendance.domain.repository.SessionRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -9,9 +9,9 @@ @RequiredArgsConstructor public class MeetingDeleteService { - private final MeetingRepository meetingRepository; + private final SessionRepository sessionRepository; - public void delete(Meeting meeting) { - meetingRepository.delete(meeting); + public void delete(Session session) { + sessionRepository.delete(session); } } diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java index 3eab97d2..08148460 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java +++ b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java @@ -1,10 +1,10 @@ package com.weeth.domain.schedule.domain.service; +import com.weeth.domain.attendance.domain.entity.Session; +import com.weeth.domain.attendance.domain.entity.enums.SessionStatus; +import com.weeth.domain.attendance.domain.repository.SessionRepository; import com.weeth.domain.schedule.application.dto.ScheduleDTO; import com.weeth.domain.schedule.application.mapper.ScheduleMapper; -import com.weeth.domain.schedule.domain.entity.Meeting; -import com.weeth.domain.schedule.domain.entity.enums.MeetingStatus; -import com.weeth.domain.schedule.domain.repository.MeetingRepository; import com.weeth.domain.schedule.application.exception.MeetingNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -16,39 +16,39 @@ @RequiredArgsConstructor public class MeetingGetService { - private final MeetingRepository meetingRepository; + private final SessionRepository sessionRepository; private final ScheduleMapper mapper; - public Meeting find(Long meetingId) { - return meetingRepository.findById(meetingId) + public Session find(Long sessionId) { + return sessionRepository.findById(sessionId) .orElseThrow(MeetingNotFoundException::new); } public List find(LocalDateTime start, LocalDateTime end) { - return meetingRepository.findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start).stream() - .map(meeting -> mapper.toScheduleDTO(meeting, true)) + return sessionRepository.findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start).stream() + .map(session -> mapper.toScheduleDTO(session, true)) .toList(); } - public List find(Integer cardinal) { - return meetingRepository.findAllByCardinalOrderByStartAsc(cardinal); + public List find(Integer cardinal) { + return sessionRepository.findAllByCardinalOrderByStartAsc(cardinal); } - public List findMeetingByCardinal(Integer cardinal) { - return meetingRepository.findAllByCardinalOrderByStartDesc(cardinal); + public List findMeetingByCardinal(Integer cardinal) { + return sessionRepository.findAllByCardinalOrderByStartDesc(cardinal); } - public List findAll() { - return meetingRepository.findAllByOrderByStartDesc(); + public List findAll() { + return sessionRepository.findAllByOrderByStartDesc(); } public List findByCardinal(Integer cardinal) { - return meetingRepository.findAllByCardinal(cardinal).stream() - .map(meeting -> mapper.toScheduleDTO(meeting, true)) + return sessionRepository.findAllByCardinal(cardinal).stream() + .map(session -> mapper.toScheduleDTO(session, true)) .toList(); } - public List findAllOpenMeetingsBeforeNow() { - return meetingRepository.findAllByMeetingStatusAndEndBeforeOrderByEndAsc(MeetingStatus.OPEN, LocalDateTime.now()); + public List findAllOpenMeetingsBeforeNow() { + return sessionRepository.findAllByStatusAndEndBeforeOrderByEndAsc(SessionStatus.OPEN, LocalDateTime.now()); } } diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingSaveService.java b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingSaveService.java index ba671f62..d31984b0 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingSaveService.java +++ b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingSaveService.java @@ -1,7 +1,7 @@ package com.weeth.domain.schedule.domain.service; -import com.weeth.domain.schedule.domain.entity.Meeting; -import com.weeth.domain.schedule.domain.repository.MeetingRepository; +import com.weeth.domain.attendance.domain.entity.Session; +import com.weeth.domain.attendance.domain.repository.SessionRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -9,9 +9,9 @@ @RequiredArgsConstructor public class MeetingSaveService { - private final MeetingRepository meetingRepository; + private final SessionRepository sessionRepository; - public void save(Meeting meeting) { - meetingRepository.save(meeting); + public void save(Session session) { + sessionRepository.save(session); } } diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java index e89301c7..e16ccd92 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java +++ b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java @@ -1,14 +1,14 @@ package com.weeth.domain.schedule.domain.service; +import com.weeth.domain.attendance.domain.entity.Session; import com.weeth.domain.schedule.application.dto.ScheduleDTO; -import com.weeth.domain.schedule.domain.entity.Meeting; import com.weeth.domain.user.domain.entity.User; import org.springframework.stereotype.Service; @Service public class MeetingUpdateService { - public void update(ScheduleDTO.Update dto, User user, Meeting meeting) { - meeting.update(dto, user); + public void update(ScheduleDTO.Update dto, User user, Session session) { + session.updateInfo(dto.title(), dto.content(), dto.location(), dto.requiredItem(), dto.start(), dto.end(), user); } } From 37a790c0b8114e20fe3ccaff4a6d1e2fb01bd181 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 01:42:44 +0900 Subject: [PATCH 08/62] =?UTF-8?q?refactor:=20UserManageUseCase=20Session?= =?UTF-8?q?=20=EC=B0=B8=EC=A1=B0=EB=A1=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/UserManageUseCaseImpl.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java b/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java index b9e1b630..e2b314c0 100644 --- a/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java +++ b/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java @@ -2,7 +2,7 @@ import jakarta.transaction.Transactional; import com.weeth.domain.attendance.domain.service.AttendanceSaveService; -import com.weeth.domain.schedule.domain.entity.Meeting; +import com.weeth.domain.attendance.domain.entity.Session; import com.weeth.domain.schedule.domain.service.MeetingGetService; import com.weeth.domain.user.application.exception.InvalidUserOrderException; import com.weeth.domain.user.application.mapper.UserMapper; @@ -95,8 +95,8 @@ public void accept(UserId userIds) { if (user.isInactive()) { userUpdateService.accept(user); - List meetings = meetingGetService.find(cardinal); - attendanceSaveService.init(user, meetings); + List sessions = meetingGetService.find(cardinal); + attendanceSaveService.init(user, sessions); } }); } @@ -140,8 +140,8 @@ public void applyOB(List requests) { if (userCardinalGetService.notContains(user, nextCardinal)) { if (userCardinalGetService.isCurrent(user, nextCardinal)) { user.initAttendance(); - List meetings = meetingGetService.find(request.cardinal()); - attendanceSaveService.init(user, meetings); + List sessionList = meetingGetService.find(request.cardinal()); + attendanceSaveService.init(user, sessionList); } UserCardinal userCardinal = new UserCardinal(user, nextCardinal); From c04b4c754862d30f870dd8afcf30396787192e88 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 01:43:42 +0900 Subject: [PATCH 09/62] =?UTF-8?q?test:=20=EB=AC=B8=EB=B2=95=20=EB=B3=80?= =?UTF-8?q?=ED=99=98=EC=97=90=20=EB=A7=9E=EC=B6=B0=20Schedule=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20=ED=94=BD=EC=8A=A4=EC=B2=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapper/AttendanceMapperTest.kt | 16 +-- .../usecase/AttendanceUseCaseImplTest.kt | 122 +++++++++--------- .../attendance/domain/entity/SessionTest.kt | 26 ++++ .../repository/AttendanceRepositoryTest.kt | 46 ++++--- .../service/AttendanceSaveServiceTest.kt | 15 ++- .../service/AttendanceUpdateServiceTest.kt | 30 ++--- .../fixture/AttendanceTestFixture.kt | 60 +++++---- .../usecase/MeetingUseCaseImplTest.kt | 70 +++++----- .../schedule/domain/entity/MeetingTest.kt | 17 --- .../schedule/fixture/ScheduleTestFixture.kt | 39 +++--- .../usecase/UserManageUseCaseTest.kt | 6 +- 11 files changed, 225 insertions(+), 222 deletions(-) create mode 100644 src/test/kotlin/com/weeth/domain/attendance/domain/entity/SessionTest.kt delete mode 100644 src/test/kotlin/com/weeth/domain/schedule/domain/entity/MeetingTest.kt diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt index 2c38f017..9816bd88 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt @@ -4,7 +4,7 @@ import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUse import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUserWithAttendances import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createAdminUserWithAttendances import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createAttendance -import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createOneDayMeeting +import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createOneDaySession import com.weeth.domain.attendance.fixture.AttendanceTestFixture.enrichUserProfile import com.weeth.domain.attendance.fixture.AttendanceTestFixture.setAttendanceId import com.weeth.domain.attendance.fixture.AttendanceTestFixture.setUserAttendanceStats @@ -24,7 +24,7 @@ class AttendanceMapperTest : describe("toMainDto") { it("사용자 + 당일 출석 객체를 Main DTO로 매핑한다") { val today = LocalDate.now() - val meeting = createOneDayMeeting(today, 1, 1111, "Today") + val meeting = createOneDaySession(today, 1, 1111, "Today") val user = createActiveUserWithAttendances("이지훈", listOf(meeting)) val attendance = user.attendances[0] @@ -52,7 +52,7 @@ class AttendanceMapperTest : it("일반 유저는 출석 코드가 null로 매핑된다") { val today = LocalDate.now() - val meeting = createOneDayMeeting(today, 1, 1234, "Today") + val meeting = createOneDaySession(today, 1, 1234, "Today") val user = createActiveUserWithAttendances("일반유저", listOf(meeting)) val attendance = user.attendances[0] @@ -67,7 +67,7 @@ class AttendanceMapperTest : describe("toResponseDto") { it("단일 출석을 Response DTO로 매핑한다") { - val meeting = createOneDayMeeting(LocalDate.now().minusDays(1), 1, 2222, "D-1") + val meeting = createOneDaySession(LocalDate.now().minusDays(1), 1, 2222, "D-1") val user = createActiveUser("사용자A") val attendance = createAttendance(meeting, user) @@ -84,8 +84,8 @@ class AttendanceMapperTest : describe("toDetailDto") { it("사용자 + Response 리스트를 Detail DTO로 매핑(total = attend + absence)") { val base = LocalDate.now() - val m1 = createOneDayMeeting(base.minusDays(2), 1, 1000, "D-2") - val m2 = createOneDayMeeting(base.minusDays(1), 1, 1001, "D-1") + val m1 = createOneDaySession(base.minusDays(2), 1, 1000, "D-2") + val m2 = createOneDaySession(base.minusDays(1), 1, 1001, "D-1") val user = createActiveUser("이지훈") setUserAttendanceStats(user, 3, 2) @@ -105,7 +105,7 @@ class AttendanceMapperTest : describe("toAttendanceInfoDto") { it("Attendance를 Info DTO로 매핑") { - val meeting = createOneDayMeeting(LocalDate.now(), 1, 3333, "Info") + val meeting = createOneDaySession(LocalDate.now(), 1, 3333, "Info") val user = createActiveUser("유저B") enrichUserProfile(user, Position.BE, "컴퓨터공학과", "20201234") @@ -125,7 +125,7 @@ class AttendanceMapperTest : it("ADMIN 유저는 출석 코드가 포함된다") { val today = LocalDate.now() val expectedCode = 1234 - val meeting = createOneDayMeeting(today, 1, expectedCode, "Today") + val meeting = createOneDaySession(today, 1, expectedCode, "Today") val adminUser = createAdminUserWithAttendances("관리자", listOf(meeting)) val attendance = adminUser.attendances[0] diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImplTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImplTest.kt index 1803e623..064abda6 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImplTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/AttendanceUseCaseImplTest.kt @@ -5,14 +5,14 @@ import com.weeth.domain.attendance.application.exception.AttendanceCodeMismatchE import com.weeth.domain.attendance.application.exception.AttendanceNotFoundException import com.weeth.domain.attendance.application.mapper.AttendanceMapper import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.enums.Status +import com.weeth.domain.attendance.domain.entity.Session +import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus import com.weeth.domain.attendance.domain.service.AttendanceGetService import com.weeth.domain.attendance.domain.service.AttendanceUpdateService import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUserWithAttendances -import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createInProgressMeeting -import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createOneDayMeeting +import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createInProgressSession +import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createOneDaySession import com.weeth.domain.schedule.application.exception.MeetingNotFoundException -import com.weeth.domain.schedule.domain.entity.Meeting import com.weeth.domain.schedule.domain.service.MeetingGetService import com.weeth.domain.user.domain.entity.Cardinal import com.weeth.domain.user.domain.entity.User @@ -53,19 +53,19 @@ class AttendanceUseCaseImplTest : it("여러 날짜의 출석 목록 중 시작/종료 날짜가 모두 오늘인 출석정보를 선택") { val today = LocalDate.now() - val meetingYesterday = createOneDayMeeting(today.minusDays(1), 1, 1111, "Yesterday") - val meetingToday = createOneDayMeeting(today, 1, 2222, "Today") - val meetingTomorrow = createOneDayMeeting(today.plusDays(1), 1, 3333, "Tomorrow") + val sessionYesterday = createOneDaySession(today.minusDays(1), 1, 1111, "Yesterday") + val sessionToday = createOneDaySession(today, 1, 2222, "Today") + val sessionTomorrow = createOneDaySession(today.plusDays(1), 1, 3333, "Tomorrow") val user = createActiveUserWithAttendances( "이지훈", - listOf(meetingYesterday, meetingToday, meetingTomorrow), + listOf(sessionYesterday, sessionToday, sessionTomorrow), ) val expectedTodayAttendance = user.attendances.first { - it.meeting.title == "Today" + it.session.title == "Today" } val mapped = mockk() @@ -82,13 +82,13 @@ class AttendanceUseCaseImplTest : it("시작/종료 날짜가 모두 오늘인 출석이 없다면 mapper.toMainDto(user, null)을 호출") { val today = LocalDate.now() - val yesterdayMeeting = createOneDayMeeting(today.minusDays(1), 1, 1111, "Yesterday") - val tomorrowMeeting = createOneDayMeeting(today.plusDays(1), 1, 3333, "Tomorrow") + val sessionYesterday = createOneDaySession(today.minusDays(1), 1, 1111, "Yesterday") + val sessionTomorrow = createOneDaySession(today.plusDays(1), 1, 3333, "Tomorrow") val user = createActiveUserWithAttendances( "이지훈", - listOf(yesterdayMeeting, tomorrowMeeting), + listOf(sessionYesterday, sessionTomorrow), ) val mapped = mockk() @@ -104,19 +104,18 @@ class AttendanceUseCaseImplTest : describe("checkIn") { context("10분 전부터 출석이 가능한지 확인") { - it("5분 뒤 시작 회의에 출석 성공") { + it("5분 뒤 시작 세션에 출석 성공") { val now = LocalDateTime.now() - val meeting = - Meeting - .builder() - .start(now.plusMinutes(5)) - .end(now.plusHours(2)) - .code(1234) - .title("Today") - .cardinal(1) - .build() - - val user = createActiveUserWithAttendances("이지훈", listOf(meeting)) + val session = + Session( + start = now.plusMinutes(5), + end = now.plusHours(2), + code = 1234, + title = "Today", + cardinal = 1, + ) + + val user = createActiveUserWithAttendances("이지훈", listOf(session)) every { userGetService.find(userId) } returns user @@ -128,17 +127,16 @@ class AttendanceUseCaseImplTest : it("11분 전에 출석시 오류 발생") { val now = LocalDateTime.now() - val meeting = - Meeting - .builder() - .start(now.plusMinutes(11)) - .end(now.plusHours(2)) - .code(1234) - .title("Today") - .cardinal(1) - .build() - - val user = createActiveUserWithAttendances("이지훈", listOf(meeting)) + val session = + Session( + start = now.plusMinutes(11), + end = now.plusHours(2), + code = 1234, + title = "Today", + cardinal = 1, + ) + + val user = createActiveUserWithAttendances("이지훈", listOf(session)) every { userGetService.find(userId) } returns user @@ -148,14 +146,14 @@ class AttendanceUseCaseImplTest : } } - context("진행 중 정기모임이고 코드 일치하며 상태가 ATTEND가 아닐 때") { + context("진행 중 세션이고 코드 일치하며 상태가 ATTEND가 아닐 때") { it("출석 처리된다") { val user = mockk() - val inProgressMeeting = createInProgressMeeting(1, 1234, "InProgress") + val inProgressSession = createInProgressSession(1, 1234, "InProgress") val attendance = mockk() - every { attendance.meeting } returns inProgressMeeting + every { attendance.session } returns inProgressSession every { attendance.isWrong(1234) } returns false - every { attendance.status } returns Status.PENDING + every { attendance.status } returns AttendanceStatus.PENDING every { userGetService.find(userId) } returns user every { user.attendances } returns listOf(attendance) @@ -166,7 +164,7 @@ class AttendanceUseCaseImplTest : } } - context("진행 중 정기모임이 없을 때") { + context("진행 중 세션이 없을 때") { it("AttendanceNotFoundException") { val user = mockk() every { userGetService.find(userId) } returns user @@ -181,10 +179,10 @@ class AttendanceUseCaseImplTest : context("코드 불일치 시") { it("AttendanceCodeMismatchException") { val user = mockk() - val inProgressMeeting = createInProgressMeeting(1, 1234, "InProgress") + val inProgressSession = createInProgressSession(1, 1234, "InProgress") val attendance = mockk() - every { attendance.meeting } returns inProgressMeeting + every { attendance.session } returns inProgressSession every { attendance.isWrong(9999) } returns true every { userGetService.find(userId) } returns user @@ -199,12 +197,12 @@ class AttendanceUseCaseImplTest : context("이미 ATTEND일 때") { it("추가 처리 없이 종료") { val user = mockk() - val inProgressMeeting = createInProgressMeeting(1, 1234, "InProgress") + val inProgressSession = createInProgressSession(1, 1234, "InProgress") val attendance = mockk() - every { attendance.meeting } returns inProgressMeeting + every { attendance.session } returns inProgressSession every { attendance.isWrong(1234) } returns false - every { attendance.status } returns Status.ATTEND + every { attendance.status } returns AttendanceStatus.ATTEND every { userGetService.find(userId) } returns user every { user.attendances } returns listOf(attendance) @@ -219,9 +217,9 @@ class AttendanceUseCaseImplTest : describe("findAllDetailsByCurrentCardinal") { it("현재 기수만 필터링·정렬하여 Detail 매핑") { val today = LocalDate.now() - val meetingDayMinus1 = createOneDayMeeting(today.minusDays(1), 1, 1111, "D-1") - val meetingToday = createOneDayMeeting(today, 1, 2222, "D-Day") - val user = createActiveUserWithAttendances("이지훈", listOf(meetingDayMinus1, meetingToday)) + val sessionDayMinus1 = createOneDaySession(today.minusDays(1), 1, 1111, "D-1") + val sessionToday = createOneDaySession(today, 1, 2222, "D-Day") + val user = createActiveUserWithAttendances("이지훈", listOf(sessionDayMinus1, sessionToday)) val userAttendances = user.attendances val attendanceFirst = userAttendances[0] @@ -253,16 +251,16 @@ class AttendanceUseCaseImplTest : } describe("close") { - it("당일 정기모임을 찾아 close") { + it("당일 세션을 찾아 close") { val now = LocalDate.now() - val targetMeeting = createOneDayMeeting(now, 1, 1111, "Today") - val otherMeeting = createOneDayMeeting(now.minusDays(1), 1, 9999, "Yesterday") + val targetSession = createOneDaySession(now, 1, 1111, "Today") + val otherSession = createOneDaySession(now.minusDays(1), 1, 9999, "Yesterday") val attendance1 = mockk() val attendance2 = mockk() - every { meetingGetService.find(1) } returns listOf(targetMeeting, otherMeeting) - every { attendanceGetService.findAllByMeeting(targetMeeting) } returns listOf(attendance1, attendance2) + every { meetingGetService.find(1) } returns listOf(targetSession, otherSession) + every { attendanceGetService.findAllByMeeting(targetSession) } returns listOf(attendance1, attendance2) attendanceUseCase.close(now, 1) @@ -273,11 +271,11 @@ class AttendanceUseCaseImplTest : } } - it("당일 정기모임이 없으면 MeetingNotFoundException") { + it("당일 세션이 없으면 MeetingNotFoundException") { val now = LocalDate.now() - val otherDayMeeting = createOneDayMeeting(now.minusDays(1), 1, 9999, "Yesterday") + val otherDaySession = createOneDaySession(now.minusDays(1), 1, 9999, "Yesterday") - every { meetingGetService.find(1) } returns listOf(otherDayMeeting) + every { meetingGetService.find(1) } returns listOf(otherDaySession) shouldThrow { attendanceUseCase.close(now, 1) @@ -286,20 +284,20 @@ class AttendanceUseCaseImplTest : } describe("findAllAttendanceByMeeting") { - it("해당 정기모임의 출석 목록을 매핑하여 반환한다") { - val meetingId = 1L - val meeting = createOneDayMeeting(LocalDate.now(), 1, 1234, "Today") + it("해당 세션의 출석 목록을 매핑하여 반환한다") { + val sessionId = 1L + val session = createOneDaySession(LocalDate.now(), 1, 1234, "Today") val attendance1 = mockk() val attendance2 = mockk() val info1 = mockk() val info2 = mockk() - every { meetingGetService.find(meetingId) } returns meeting - every { attendanceGetService.findAllByMeeting(meeting) } returns listOf(attendance1, attendance2) + every { meetingGetService.find(sessionId) } returns session + every { attendanceGetService.findAllByMeeting(session) } returns listOf(attendance1, attendance2) every { attendanceMapper.toAttendanceInfoDto(attendance1) } returns info1 every { attendanceMapper.toAttendanceInfoDto(attendance2) } returns info2 - val result = attendanceUseCase.findAllAttendanceByMeeting(meetingId) + val result = attendanceUseCase.findAllAttendanceByMeeting(sessionId) result.size shouldBe 2 result[0] shouldBe info1 diff --git a/src/test/kotlin/com/weeth/domain/attendance/domain/entity/SessionTest.kt b/src/test/kotlin/com/weeth/domain/attendance/domain/entity/SessionTest.kt new file mode 100644 index 00000000..722e6253 --- /dev/null +++ b/src/test/kotlin/com/weeth/domain/attendance/domain/entity/SessionTest.kt @@ -0,0 +1,26 @@ +package com.weeth.domain.attendance.domain.entity + +import com.weeth.domain.attendance.domain.entity.enums.SessionStatus +import com.weeth.domain.schedule.fixture.ScheduleTestFixture +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe + +class SessionTest : + StringSpec({ + "close는 status를 CLOSED로 변경한다" { + val session = ScheduleTestFixture.createSession(status = SessionStatus.OPEN) + + session.close() + + session.status shouldBe SessionStatus.CLOSED + } + + "이미 CLOSED 상태에서 close 호출 시 예외가 발생한다" { + val session = ScheduleTestFixture.createSession(status = SessionStatus.CLOSED) + + shouldThrow { + session.close() + } + } + }) diff --git a/src/test/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepositoryTest.kt b/src/test/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepositoryTest.kt index f63aaff8..562249f3 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepositoryTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepositoryTest.kt @@ -2,9 +2,8 @@ package com.weeth.domain.attendance.domain.repository import com.weeth.config.TestContainersConfig import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.schedule.domain.entity.Meeting -import com.weeth.domain.schedule.domain.entity.enums.MeetingStatus -import com.weeth.domain.schedule.domain.repository.MeetingRepository +import com.weeth.domain.attendance.domain.entity.Session +import com.weeth.domain.attendance.domain.entity.enums.SessionStatus import com.weeth.domain.user.domain.entity.User import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.repository.UserRepository @@ -22,26 +21,25 @@ import java.time.LocalDateTime @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) class AttendanceRepositoryTest( private val attendanceRepository: AttendanceRepository, - private val meetingRepository: MeetingRepository, + private val sessionRepository: SessionRepository, private val userRepository: UserRepository, ) : DescribeSpec({ - lateinit var meeting: Meeting + lateinit var session: Session lateinit var activeUser1: User lateinit var activeUser2: User beforeEach { - meeting = - Meeting - .builder() - .title("1차 정기모임") - .start(LocalDateTime.now().minusHours(1)) - .end(LocalDateTime.now().plusHours(1)) - .code(1234) - .cardinal(1) - .meetingStatus(MeetingStatus.OPEN) - .build() - meetingRepository.save(meeting) + session = + Session( + title = "1차 정기모임", + start = LocalDateTime.now().minusHours(1), + end = LocalDateTime.now().plusHours(1), + code = 1234, + cardinal = 1, + status = SessionStatus.OPEN, + ) + sessionRepository.save(session) activeUser1 = User @@ -60,22 +58,22 @@ class AttendanceRepositoryTest( activeUser2.accept() userRepository.saveAll(listOf(activeUser1, activeUser2)) - attendanceRepository.save(Attendance(meeting, activeUser1)) - attendanceRepository.save(Attendance(meeting, activeUser2)) + attendanceRepository.save(Attendance.create(session, activeUser1)) + attendanceRepository.save(Attendance.create(session, activeUser2)) } - describe("findAllByMeetingAndUserStatus") { - it("특정 정기모임 + 사용자 상태로 출석 목록 조회") { - val attendances = attendanceRepository.findAllByMeetingAndUserStatus(meeting, Status.ACTIVE) + describe("findAllBySessionAndUserStatus") { + it("특정 세션 + 사용자 상태로 출석 목록 조회") { + val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) attendances shouldHaveSize 2 attendances.map { it.user.name } shouldContainExactlyInAnyOrder listOf("이지훈", "이강혁") } } - describe("deleteAllByMeeting") { - it("특정 정기모임의 모든 출석 레코드 삭제") { - attendanceRepository.deleteAllByMeeting(meeting) + describe("deleteAllBySession") { + it("특정 세션의 모든 출석 레코드 삭제") { + attendanceRepository.deleteAllBySession(session) attendanceRepository.findAll().shouldBeEmpty() } diff --git a/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt b/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt index 5d8db16a..baa9ae4b 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt @@ -3,7 +3,7 @@ package com.weeth.domain.attendance.domain.service import com.weeth.domain.attendance.domain.entity.Attendance import com.weeth.domain.attendance.domain.repository.AttendanceRepository import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUser -import com.weeth.domain.schedule.fixture.ScheduleTestFixture.createMeeting +import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createOneDaySession import io.kotest.core.spec.style.DescribeSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe @@ -11,6 +11,7 @@ import io.mockk.every import io.mockk.mockk import io.mockk.slot import io.mockk.verify +import java.time.LocalDate class AttendanceSaveServiceTest : DescribeSpec({ @@ -21,12 +22,12 @@ class AttendanceSaveServiceTest : describe("init") { it("각 정기모임에 대한 Attendance 저장 후 user.add 호출") { val user = mockk(relaxUnitFun = true) - val meetingFirst = createMeeting() - val meetingSecond = createMeeting() + val sessionFirst = createOneDaySession(LocalDate.now(), 1, 1234, "1차") + val sessionSecond = createOneDaySession(LocalDate.now().plusDays(7), 1, 5678, "2차") every { attendanceRepository.save(any()) } answers { firstArg() } - attendanceSaveService.init(user, listOf(meetingFirst, meetingSecond)) + attendanceSaveService.init(user, listOf(sessionFirst, sessionSecond)) verify(exactly = 2) { attendanceRepository.save(any()) } verify(exactly = 2) { user.add(any()) } @@ -35,18 +36,18 @@ class AttendanceSaveServiceTest : describe("saveAll") { it("사용자 수만큼 Attendance 생성 후 saveAll 호출") { - val meeting = createMeeting() + val session = createOneDaySession(LocalDate.now(), 1, 1234, "1차") val userFirst = createActiveUser("이지훈") val userSecond = createActiveUser("이강혁") val listSlot = slot>() every { attendanceRepository.saveAll(capture(listSlot)) } answers { firstArg() } - attendanceSaveService.saveAll(listOf(userFirst, userSecond), meeting) + attendanceSaveService.saveAll(listOf(userFirst, userSecond), session) val savedAttendances = listSlot.captured savedAttendances shouldHaveSize 2 - savedAttendances.forEach { it.meeting shouldBe meeting } + savedAttendances.forEach { it.session shouldBe session } savedAttendances.map { it.user } shouldBe listOf(userFirst, userSecond) } } diff --git a/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceUpdateServiceTest.kt b/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceUpdateServiceTest.kt index 13ac8305..835be9e1 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceUpdateServiceTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceUpdateServiceTest.kt @@ -1,14 +1,14 @@ package com.weeth.domain.attendance.domain.service -import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.enums.Status +import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUser import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createAttendance -import com.weeth.domain.schedule.fixture.ScheduleTestFixture.createMeeting +import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createOneDaySession import io.kotest.core.spec.style.DescribeSpec import io.mockk.every import io.mockk.spyk import io.mockk.verify +import java.time.LocalDate class AttendanceUpdateServiceTest : DescribeSpec({ @@ -17,11 +17,11 @@ class AttendanceUpdateServiceTest : describe("attend") { it("attendance.attend() + user.attend()을 호출한다") { - val meeting = createMeeting() + val session = createOneDaySession(LocalDate.now(), 1, 1234, "1차") val userSpy = spyk(createActiveUser("이지훈")) every { userSpy.attend() } returns Unit - val attendanceSpy = spyk(createAttendance(meeting, userSpy)) + val attendanceSpy = spyk(createAttendance(session, userSpy)) attendanceUpdateService.attend(attendanceSpy) @@ -32,17 +32,17 @@ class AttendanceUpdateServiceTest : describe("close") { it("pending만 close() + user.absent()을 호출한다") { - val meeting = createMeeting() + val session = createOneDaySession(LocalDate.now(), 1, 1234, "1차") val pendingUserSpy = spyk(createActiveUser("pending-user")) val nonPendingUserSpy = spyk(createActiveUser("non-pending-user")) every { pendingUserSpy.absent() } returns Unit every { nonPendingUserSpy.absent() } returns Unit - val pendingAttendanceSpy = spyk(createAttendance(meeting, pendingUserSpy)) - val nonPendingAttendanceSpy = spyk(createAttendance(meeting, nonPendingUserSpy)) - every { pendingAttendanceSpy.isPending } returns true - every { nonPendingAttendanceSpy.isPending } returns false + val pendingAttendanceSpy = spyk(createAttendance(session, pendingUserSpy)) + val nonPendingAttendanceSpy = spyk(createAttendance(session, nonPendingUserSpy)) + every { pendingAttendanceSpy.isPending() } returns true + every { nonPendingAttendanceSpy.isPending() } returns false attendanceUpdateService.close(listOf(pendingAttendanceSpy, nonPendingAttendanceSpy)) @@ -56,17 +56,17 @@ class AttendanceUpdateServiceTest : describe("updateUserAttendanceByStatus") { it("ATTEND면 user.removeAttend(), 그 외에는 user.removeAbsent()") { - val meeting = createMeeting() + val session = createOneDaySession(LocalDate.now(), 1, 1234, "1차") val attendUserSpy = spyk(createActiveUser("attend-user")) val absentUserSpy = spyk(createActiveUser("absent-user")) every { attendUserSpy.removeAttend() } returns Unit every { absentUserSpy.removeAbsent() } returns Unit - val attendAttendanceSpy = spyk(createAttendance(meeting, attendUserSpy)) - val absentAttendanceSpy = spyk(createAttendance(meeting, absentUserSpy)) - every { attendAttendanceSpy.status } returns Status.ATTEND - every { absentAttendanceSpy.status } returns Status.ABSENT + val attendAttendanceSpy = spyk(createAttendance(session, attendUserSpy)) + val absentAttendanceSpy = spyk(createAttendance(session, absentUserSpy)) + every { attendAttendanceSpy.status } returns AttendanceStatus.ATTEND + every { absentAttendanceSpy.status } returns AttendanceStatus.ABSENT every { attendAttendanceSpy.user } returns attendUserSpy every { absentAttendanceSpy.user } returns absentUserSpy diff --git a/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt b/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt index 864321cf..99844904 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt @@ -1,7 +1,7 @@ package com.weeth.domain.attendance.fixture import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.schedule.domain.entity.Meeting +import com.weeth.domain.attendance.domain.entity.Session import com.weeth.domain.user.domain.entity.User import com.weeth.domain.user.domain.entity.enums.Department import com.weeth.domain.user.domain.entity.enums.Position @@ -29,12 +29,12 @@ object AttendanceTestFixture { fun createActiveUserWithAttendances( name: String, - meetings: List, + sessions: List, ): User { val user = createActiveUser(name) initAttendancesField(user) - meetings.forEach { meeting -> - val attendance = createAttendance(meeting, user) + sessions.forEach { session -> + val attendance = createAttendance(session, user) user.add(attendance) } return user @@ -42,52 +42,50 @@ object AttendanceTestFixture { fun createAdminUserWithAttendances( name: String, - meetings: List, + sessions: List, ): User { val user = createAdminUser(name) initAttendancesField(user) - meetings.forEach { meeting -> - val attendance = createAttendance(meeting, user) + sessions.forEach { session -> + val attendance = createAttendance(session, user) user.add(attendance) } return user } fun createAttendance( - meeting: Meeting, + session: Session, user: User, - ): Attendance = Attendance(meeting, user) + ): Attendance = Attendance.create(session, user) - fun createOneDayMeeting( + fun createOneDaySession( date: LocalDate, cardinal: Int, code: Int, title: String, - ): Meeting = - Meeting - .builder() - .title(title) - .location("Test Location") - .start(date.atTime(10, 0)) - .end(date.atTime(12, 0)) - .code(code) - .cardinal(cardinal) - .build() + ): Session = + Session( + title = title, + location = "Test Location", + start = date.atTime(10, 0), + end = date.atTime(12, 0), + code = code, + cardinal = cardinal, + ) - fun createInProgressMeeting( + fun createInProgressSession( cardinal: Int, code: Int, title: String, - ): Meeting = - Meeting - .builder() - .title(title) - .location("Test Location") - .start(LocalDateTime.now().minusMinutes(5)) - .end(LocalDateTime.now().plusMinutes(5)) - .code(code) - .cardinal(cardinal) - .build() + ): Session = + Session( + title = title, + location = "Test Location", + start = LocalDateTime.now().minusMinutes(5), + end = LocalDateTime.now().plusMinutes(5), + code = code, + cardinal = cardinal, + ) fun setAttendanceId( attendance: Attendance, diff --git a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt index d5b2ac79..0b2130c0 100644 --- a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt +++ b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt @@ -1,7 +1,8 @@ package com.weeth.domain.schedule.application.usecase import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.enums.Status +import com.weeth.domain.attendance.domain.entity.Session +import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus import com.weeth.domain.attendance.domain.service.AttendanceDeleteService import com.weeth.domain.attendance.domain.service.AttendanceGetService import com.weeth.domain.attendance.domain.service.AttendanceSaveService @@ -9,7 +10,6 @@ import com.weeth.domain.attendance.domain.service.AttendanceUpdateService import com.weeth.domain.schedule.application.dto.MeetingDTO import com.weeth.domain.schedule.application.dto.ScheduleDTO import com.weeth.domain.schedule.application.mapper.MeetingMapper -import com.weeth.domain.schedule.domain.entity.Meeting import com.weeth.domain.schedule.domain.entity.enums.Type import com.weeth.domain.schedule.domain.service.MeetingDeleteService import com.weeth.domain.schedule.domain.service.MeetingGetService @@ -83,56 +83,56 @@ class MeetingUseCaseImplTest : ) } - describe("find(userId, meetingId)") { - val meetingId = 1L + describe("find(userId, sessionId)") { + val sessionId = 1L val userId = 10L - val meeting = ScheduleTestFixture.createMeeting(id = meetingId) + val session = ScheduleTestFixture.createSession(id = sessionId) context("ADMIN 유저일 때") { it("toAdminResponse로 매핑한다") { val adminUser = mockk() every { adminUser.role } returns Role.ADMIN every { userGetService.find(userId) } returns adminUser - every { meetingGetService.find(meetingId) } returns meeting + every { meetingGetService.find(sessionId) } returns session val adminResponse = mockk() - every { meetingMapper.toAdminResponse(meeting) } returns adminResponse + every { meetingMapper.toAdminResponse(session) } returns adminResponse - val result = useCase.find(userId, meetingId) + val result = useCase.find(userId, sessionId) result shouldBe adminResponse - verify { meetingMapper.toAdminResponse(meeting) } + verify { meetingMapper.toAdminResponse(session) } } } context("일반 유저일 때") { - it("to(meeting)으로 매핑한다 (코드 미노출)") { + it("to(session)으로 매핑한다 (코드 미노출)") { val normalUser = mockk() every { normalUser.role } returns Role.USER every { userGetService.find(userId) } returns normalUser - every { meetingGetService.find(meetingId) } returns meeting + every { meetingGetService.find(sessionId) } returns session val normalResponse = mockk() - every { meetingMapper.to(meeting) } returns normalResponse + every { meetingMapper.to(session) } returns normalResponse - val result = useCase.find(userId, meetingId) + val result = useCase.find(userId, sessionId) result shouldBe normalResponse - verify { meetingMapper.to(meeting) } + verify { meetingMapper.to(session) } } } } describe("find(cardinal)") { - it("이번 주 정기모임이 있으면 thisWeek에 포함된다") { + it("이번 주 세션이 있으면 thisWeek에 포함된다") { val now = LocalDateTime.now() - val thisWeekMeeting = - ScheduleTestFixture.createMeeting( + val thisWeekSession = + ScheduleTestFixture.createSession( id = 1L, title = "This Week", start = now, end = now.plusHours(2), ) - val lastWeekMeeting = - ScheduleTestFixture.createMeeting( + val lastWeekSession = + ScheduleTestFixture.createSession( id = 2L, title = "Last Week", start = now.minusDays(14), @@ -141,9 +141,9 @@ class MeetingUseCaseImplTest : val thisWeekInfo = MeetingDTO.Info(1L, 1, "This Week", now) val lastWeekInfo = MeetingDTO.Info(2L, 1, "Last Week", now.minusDays(14)) - every { meetingGetService.findMeetingByCardinal(1) } returns listOf(thisWeekMeeting, lastWeekMeeting) - every { meetingMapper.toInfo(thisWeekMeeting) } returns thisWeekInfo - every { meetingMapper.toInfo(lastWeekMeeting) } returns lastWeekInfo + every { meetingGetService.findMeetingByCardinal(1) } returns listOf(thisWeekSession, lastWeekSession) + every { meetingMapper.toInfo(thisWeekSession) } returns thisWeekInfo + every { meetingMapper.toInfo(lastWeekSession) } returns lastWeekInfo val result = useCase.find(1) @@ -153,12 +153,12 @@ class MeetingUseCaseImplTest : } describe("save") { - it("정기모임 저장 후 해당 기수 전체 유저에게 출석을 생성한다") { + it("세션 저장 후 해당 기수 전체 유저에게 출석을 생성한다") { val userId = 10L val user = mockk() val cardinal = mockk() val userList = listOf(mockk(), mockk(), mockk()) - val meeting = ScheduleTestFixture.createMeeting() + val session = ScheduleTestFixture.createSession() val dto = ScheduleDTO.Save( "Title", @@ -174,34 +174,34 @@ class MeetingUseCaseImplTest : every { userGetService.find(userId) } returns user every { cardinalGetService.findByUserSide(1) } returns cardinal every { userGetService.findAllByCardinal(cardinal) } returns userList - every { meetingMapper.from(dto, user) } returns meeting + every { meetingMapper.from(dto, user) } returns session useCase.save(dto, userId) - verify(exactly = 1) { meetingSaveService.save(meeting) } - verify(exactly = 1) { attendanceSaveService.saveAll(userList, meeting) } + verify(exactly = 1) { meetingSaveService.save(session) } + verify(exactly = 1) { attendanceSaveService.saveAll(userList, session) } } } describe("delete") { - it("출석 통계 롤백 후 출석 삭제 → 정기모임 삭제 순서로 처리한다") { - val meetingId = 1L - val meeting = ScheduleTestFixture.createMeeting(id = meetingId) + it("출석 통계 롤백 후 출석 삭제 → 세션 삭제 순서로 처리한다") { + val sessionId = 1L + val session = ScheduleTestFixture.createSession(id = sessionId) val attendance1 = mockk() val attendance2 = mockk() val attendances = listOf(attendance1, attendance2) - every { meetingGetService.find(meetingId) } returns meeting - every { attendanceGetService.findAllByMeeting(meeting) } returns attendances + every { meetingGetService.find(sessionId) } returns session + every { attendanceGetService.findAllByMeeting(session) } returns attendances - useCase.delete(meetingId) + useCase.delete(sessionId) verify(ordering = io.mockk.Ordering.ORDERED) { attendanceUpdateService.updateUserAttendanceByStatus(attendances) em.flush() em.clear() - attendanceDeleteService.deleteAll(meeting) - meetingDeleteService.delete(meeting) + attendanceDeleteService.deleteAll(session) + meetingDeleteService.delete(session) } } } diff --git a/src/test/kotlin/com/weeth/domain/schedule/domain/entity/MeetingTest.kt b/src/test/kotlin/com/weeth/domain/schedule/domain/entity/MeetingTest.kt deleted file mode 100644 index 88d1f232..00000000 --- a/src/test/kotlin/com/weeth/domain/schedule/domain/entity/MeetingTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.weeth.domain.schedule.domain.entity - -import com.weeth.domain.schedule.domain.entity.enums.MeetingStatus -import com.weeth.domain.schedule.fixture.ScheduleTestFixture -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe - -class MeetingTest : - StringSpec({ - "close는 meetingStatus를 CLOSE로 변경한다" { - val meeting = ScheduleTestFixture.createMeeting(meetingStatus = MeetingStatus.OPEN) - - meeting.close() - - meeting.meetingStatus shouldBe MeetingStatus.CLOSE - } - }) diff --git a/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt b/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt index c86f7ae2..408d22dd 100644 --- a/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt +++ b/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt @@ -1,8 +1,8 @@ package com.weeth.domain.schedule.fixture +import com.weeth.domain.attendance.domain.entity.Session +import com.weeth.domain.attendance.domain.entity.enums.SessionStatus import com.weeth.domain.schedule.domain.entity.Event -import com.weeth.domain.schedule.domain.entity.Meeting -import com.weeth.domain.schedule.domain.entity.enums.MeetingStatus import org.springframework.test.util.ReflectionTestUtils import java.time.LocalDateTime @@ -31,30 +31,29 @@ object ScheduleTestFixture { return event } - fun createMeeting( + fun createSession( id: Long = 0L, - title: String = "Test Meeting", + title: String = "Test Session", content: String = "Test Content", location: String = "Test Location", cardinal: Int = 1, code: Int = 1234, - meetingStatus: MeetingStatus = MeetingStatus.OPEN, + status: SessionStatus = SessionStatus.OPEN, start: LocalDateTime = LocalDateTime.of(2026, 3, 1, 10, 0), end: LocalDateTime = LocalDateTime.of(2026, 3, 1, 12, 0), - ): Meeting { - val meeting = - Meeting - .builder() - .title(title) - .content(content) - .location(location) - .cardinal(cardinal) - .code(code) - .meetingStatus(meetingStatus) - .start(start) - .end(end) - .build() - if (id != 0L) ReflectionTestUtils.setField(meeting, "id", id) - return meeting + ): Session { + val session = + Session( + title = title, + content = content, + location = location, + cardinal = cardinal, + code = code, + status = status, + start = start, + end = end, + ) + if (id != 0L) ReflectionTestUtils.setField(session, "id", id) + return session } } diff --git a/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt b/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt index d2164df9..b267a3e6 100644 --- a/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt +++ b/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt @@ -1,7 +1,7 @@ package com.weeth.domain.user.application.usecase +import com.weeth.domain.attendance.domain.entity.Session import com.weeth.domain.attendance.domain.service.AttendanceSaveService -import com.weeth.domain.schedule.domain.entity.Meeting import com.weeth.domain.schedule.domain.service.MeetingGetService import com.weeth.domain.user.application.dto.request.UserRequestDto import com.weeth.domain.user.application.dto.response.UserResponseDto @@ -141,7 +141,7 @@ class UserManageUseCaseTest : val user1 = UserTestFixture.createWaitingUser1(1L) val userIds = UserRequestDto.UserId(listOf(1L)) val cardinal = CardinalTestFixture.createCardinal(id = 1L, cardinalNumber = 8, year = 2020, semester = 2) - val meetings = listOf(mockk()) + val meetings = listOf(mockk()) every { userGetService.findAll(userIds.userId()) } returns listOf(user1) every { userCardinalGetService.getCurrentCardinal(user1) } returns cardinal @@ -205,7 +205,7 @@ class UserManageUseCaseTest : .build() val nextCardinal = CardinalTestFixture.createCardinal(id = 1L, cardinalNumber = 4, year = 2020, semester = 2) val request = UserRequestDto.UserApplyOB(1L, 4) - val meeting = listOf(mockk()) + val meeting = listOf(mockk()) every { userGetService.find(1L) } returns user every { cardinalGetService.findByAdminSide(4) } returns nextCardinal From cddc39ed1152a1410a522fdea88e9f7054bd24fa Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 02:10:38 +0900 Subject: [PATCH 10/62] =?UTF-8?q?refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20Status=20enum=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/weeth/domain/attendance/domain/enums/Status.kt | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/enums/Status.kt diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/enums/Status.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/enums/Status.kt deleted file mode 100644 index 6184bf45..00000000 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/enums/Status.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.weeth.domain.attendance.domain.enums - -enum class Status { - ATTEND, - PENDING, - ABSENT, -} From ea7e51c18ab90baf110a232707b0f0d913891b28 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 02:15:00 +0900 Subject: [PATCH 11/62] =?UTF-8?q?refactor:=20Schedule=20enum=20java=20->?= =?UTF-8?q?=20kotlin=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/weeth/domain/schedule/domain/entity/enums/Type.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/{java/com/weeth/domain/schedule/domain/entity/enums/Type.java => kotlin/com/weeth/domain/schedule/domain/entity/enums/Type.kt} (100%) diff --git a/src/main/java/com/weeth/domain/schedule/domain/entity/enums/Type.java b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/enums/Type.kt similarity index 100% rename from src/main/java/com/weeth/domain/schedule/domain/entity/enums/Type.java rename to src/main/kotlin/com/weeth/domain/schedule/domain/entity/enums/Type.kt From 758194c78f4c3329b7f1b8b5a5e508c0cd36af1c Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 02:16:02 +0900 Subject: [PATCH 12/62] =?UTF-8?q?refactor:=20Schedule=20enum=20=20kotlin?= =?UTF-8?q?=20=EB=AC=B8=EB=B2=95=EC=9C=BC=EB=A1=9C=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/weeth/domain/schedule/domain/entity/enums/Type.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/enums/Type.kt b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/enums/Type.kt index ca0c721d..367e6a0b 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/enums/Type.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/enums/Type.kt @@ -1,5 +1,6 @@ -package com.weeth.domain.schedule.domain.entity.enums; +package com.weeth.domain.schedule.domain.entity.enums -public enum Type { - EVENT, MEETING +enum class Type { + EVENT, + MEETING, } From cb42185bb1a284d484b7b108baf2d03a676bbe37 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 02:42:11 +0900 Subject: [PATCH 13/62] =?UTF-8?q?refactor:=20Schedule=20dto=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=20kotlin=20=EB=AC=B8=EB=B2=95=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/ScheduleSaveRequest.kt | 26 ++++++++++++++ .../dto/request/ScheduleTimeRequest.kt | 14 ++++++++ .../dto/request/ScheduleUpdateRequest.kt | 24 +++++++++++++ .../application/dto/response/EventResponse.kt | 30 ++++++++++++++++ .../dto/response/ScheduleResponse.kt | 17 ++++++++++ .../dto/response/SessionInfoResponse.kt | 15 ++++++++ .../dto/response/SessionInfosResponse.kt | 6 ++++ .../dto/response/SessionResponse.kt | 34 +++++++++++++++++++ 8 files changed, 166 insertions(+) create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleTimeRequest.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/dto/response/EventResponse.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/dto/response/ScheduleResponse.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfoResponse.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionResponse.kt diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt new file mode 100644 index 00000000..1099d68a --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt @@ -0,0 +1,26 @@ +package com.weeth.domain.schedule.application.dto.request + +import com.weeth.domain.schedule.domain.entity.enums.Type +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import org.springframework.format.annotation.DateTimeFormat +import java.time.LocalDateTime + +data class ScheduleSaveRequest( + @field:NotBlank + val title: String, + @field:NotBlank + val content: String, + @field:NotBlank + val location: String, + @field:NotNull + val type: Type, + @field:NotNull + val cardinal: Int, + @field:NotNull + @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + val start: LocalDateTime, + @field:NotNull + @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + val end: LocalDateTime, +) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleTimeRequest.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleTimeRequest.kt new file mode 100644 index 00000000..04b42642 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleTimeRequest.kt @@ -0,0 +1,14 @@ +package com.weeth.domain.schedule.application.dto.request + +import jakarta.validation.constraints.NotNull +import org.springframework.format.annotation.DateTimeFormat +import java.time.LocalDateTime + +data class ScheduleTimeRequest( + @field:NotNull + @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + val start: LocalDateTime, + @field:NotNull + @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + val end: LocalDateTime, +) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt new file mode 100644 index 00000000..1f4037e9 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt @@ -0,0 +1,24 @@ +package com.weeth.domain.schedule.application.dto.request + +import com.weeth.domain.schedule.domain.entity.enums.Type +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import org.springframework.format.annotation.DateTimeFormat +import java.time.LocalDateTime + +data class ScheduleUpdateRequest( + @field:NotBlank + val title: String, + @field:NotBlank + val content: String, + @field:NotBlank + val location: String, + @field:NotNull + val type: Type, + @field:NotNull + @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + val start: LocalDateTime, + @field:NotNull + @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + val end: LocalDateTime, +) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/EventResponse.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/EventResponse.kt new file mode 100644 index 00000000..c3041d8a --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/EventResponse.kt @@ -0,0 +1,30 @@ +package com.weeth.domain.schedule.application.dto.response + +import com.weeth.domain.schedule.domain.entity.enums.Type +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +data class EventResponse( + @field:Schema(description = "일정 ID", example = "1") + val id: Long, + @field:Schema(description = "일정 제목", example = "MT") + val title: String, + @field:Schema(description = "일정 내용") + val content: String, + @field:Schema(description = "장소", example = "공학관 401호") + val location: String, + @field:Schema(description = "작성자 이름", example = "이지훈") + val name: String?, + @field:Schema(description = "기수", example = "8") + val cardinal: Int, + @field:Schema(description = "일정 타입", example = "EVENT") + val type: Type, + @field:Schema(description = "시작 시간") + val start: LocalDateTime, + @field:Schema(description = "종료 시간") + val end: LocalDateTime, + @field:Schema(description = "생성 시간") + val createdAt: LocalDateTime?, + @field:Schema(description = "수정 시간") + val modifiedAt: LocalDateTime?, +) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/ScheduleResponse.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/ScheduleResponse.kt new file mode 100644 index 00000000..80a05f33 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/ScheduleResponse.kt @@ -0,0 +1,17 @@ +package com.weeth.domain.schedule.application.dto.response + +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +data class ScheduleResponse( + @field:Schema(description = "일정 ID", example = "1") + val id: Long, + @field:Schema(description = "제목", example = "1차 정기모임") + val title: String, + @field:Schema(description = "시작 시간") + val start: LocalDateTime, + @field:Schema(description = "종료 시간") + val end: LocalDateTime, + @field:Schema(description = "정기모임 여부") + val isMeeting: Boolean, +) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfoResponse.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfoResponse.kt new file mode 100644 index 00000000..3b5eb2cd --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfoResponse.kt @@ -0,0 +1,15 @@ +package com.weeth.domain.schedule.application.dto.response + +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +data class SessionInfoResponse( + @field:Schema(description = "정기모임 ID", example = "1") + val id: Long, + @field:Schema(description = "기수", example = "8") + val cardinal: Int, + @field:Schema(description = "제목", example = "1차 정기모임") + val title: String, + @field:Schema(description = "시작 시간") + val start: LocalDateTime, +) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt new file mode 100644 index 00000000..5fd6c007 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt @@ -0,0 +1,6 @@ +package com.weeth.domain.schedule.application.dto.response + +data class SessionInfosResponse( + val thisWeek: SessionInfoResponse?, + val meetings: List, +) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionResponse.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionResponse.kt new file mode 100644 index 00000000..a94105b4 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionResponse.kt @@ -0,0 +1,34 @@ +package com.weeth.domain.schedule.application.dto.response + +import com.fasterxml.jackson.annotation.JsonInclude +import com.weeth.domain.schedule.domain.entity.enums.Type +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +@JsonInclude(JsonInclude.Include.NON_NULL) +data class SessionResponse( + @field:Schema(description = "정기모임 ID", example = "1") + val id: Long, + @field:Schema(description = "제목", example = "1차 정기모임") + val title: String, + @field:Schema(description = "내용") + val content: String?, + @field:Schema(description = "장소", example = "공학관 401호") + val location: String?, + @field:Schema(description = "작성자 이름", example = "이지훈") + val name: String?, + @field:Schema(description = "기수", example = "8") + val cardinal: Int, + @field:Schema(description = "일정 타입", example = "MEETING") + val type: Type, + @field:Schema(description = "출석 코드", example = "1234") + val code: Int?, + @field:Schema(description = "시작 시간") + val start: LocalDateTime, + @field:Schema(description = "종료 시간") + val end: LocalDateTime, + @field:Schema(description = "생성 시간") + val createdAt: LocalDateTime?, + @field:Schema(description = "수정 시간") + val modifiedAt: LocalDateTime?, +) From 74b5837b7ee84691ebe7802793a66e9f726ebbbf Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 02:45:17 +0900 Subject: [PATCH 14/62] =?UTF-8?q?refactor:=20requiredItem=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20dto=20=EC=B0=B8?= =?UTF-8?q?=EC=A1=B0=20=ED=8C=8C=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/schedule/domain/service/EventUpdateService.java | 2 +- .../domain/schedule/domain/service/MeetingUpdateService.java | 2 +- .../com/weeth/domain/attendance/domain/entity/Session.kt | 5 ----- .../kotlin/com/weeth/domain/schedule/domain/entity/Event.kt | 5 ----- .../com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt | 1 - 5 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java b/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java index 4ff206e8..7d38bcea 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java +++ b/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java @@ -13,6 +13,6 @@ public class EventUpdateService { public void update(Event event, ScheduleDTO.Update dto, User user) { - event.update(dto.title(), dto.content(), dto.location(), dto.requiredItem(), dto.start(), dto.end(), user); + event.update(dto.title(), dto.content(), dto.location(), dto.start(), dto.end(), user); } } diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java index e16ccd92..93985fdc 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java +++ b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java @@ -9,6 +9,6 @@ public class MeetingUpdateService { public void update(ScheduleDTO.Update dto, User user, Session session) { - session.updateInfo(dto.title(), dto.content(), dto.location(), dto.requiredItem(), dto.start(), dto.end(), user); + session.updateInfo(dto.title(), dto.content(), dto.location(), dto.start(), dto.end(), user); } } diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt index 8d421540..af39a8e8 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt @@ -24,7 +24,6 @@ class Session( var content: String? = null, var location: String? = null, var cardinal: Int, - var requiredItem: String? = null, var start: LocalDateTime, var end: LocalDateTime, var code: Int, @@ -47,7 +46,6 @@ class Session( title: String, content: String?, location: String?, - requiredItem: String?, start: LocalDateTime, end: LocalDateTime, user: User?, @@ -55,7 +53,6 @@ class Session( this.title = title this.content = content this.location = location - this.requiredItem = requiredItem this.start = start this.end = end this.user = user @@ -71,7 +68,6 @@ class Session( content: String?, location: String?, cardinal: Int, - requiredItem: String?, start: LocalDateTime, end: LocalDateTime, user: User?, @@ -83,7 +79,6 @@ class Session( content = content, location = location, cardinal = cardinal, - requiredItem = requiredItem, start = start, end = end, code = generateCode(), diff --git a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt index 83394b53..741585ca 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt @@ -19,7 +19,6 @@ class Event( var content: String, var location: String, var cardinal: Int, - var requiredItem: String? = null, var start: LocalDateTime, var end: LocalDateTime, @ManyToOne(fetch = FetchType.LAZY) @@ -34,7 +33,6 @@ class Event( title: String, content: String, location: String, - requiredItem: String?, start: LocalDateTime, end: LocalDateTime, user: User?, @@ -42,7 +40,6 @@ class Event( this.title = title this.content = content this.location = location - this.requiredItem = requiredItem this.start = start this.end = end this.user = user @@ -54,7 +51,6 @@ class Event( content: String, location: String, cardinal: Int, - requiredItem: String?, start: LocalDateTime, end: LocalDateTime, user: User?, @@ -66,7 +62,6 @@ class Event( content = content, location = location, cardinal = cardinal, - requiredItem = requiredItem, start = start, end = end, user = user, diff --git a/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt b/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt index 408d22dd..d3b81085 100644 --- a/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt +++ b/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt @@ -22,7 +22,6 @@ object ScheduleTestFixture { content = content, location = location, cardinal = cardinal, - requiredItem = null, start = start, end = end, user = null, From 80fb562fd0490b78026c8e4bf3c29a6d95aceade Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 02:49:06 +0900 Subject: [PATCH 15/62] =?UTF-8?q?refactor:=20Schedule=20mapper=20java=20->?= =?UTF-8?q?=20kotlin=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/mapper/MeetingMapper.java | 39 ------------------- .../application/mapper/EventMapper.kt} | 0 .../application/mapper/ScheduleMapper.kt} | 0 3 files changed, 39 deletions(-) delete mode 100644 src/main/java/com/weeth/domain/schedule/application/mapper/MeetingMapper.java rename src/main/{java/com/weeth/domain/schedule/application/mapper/EventMapper.java => kotlin/com/weeth/domain/schedule/application/mapper/EventMapper.kt} (100%) rename src/main/{java/com/weeth/domain/schedule/application/mapper/ScheduleMapper.java => kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt} (100%) diff --git a/src/main/java/com/weeth/domain/schedule/application/mapper/MeetingMapper.java b/src/main/java/com/weeth/domain/schedule/application/mapper/MeetingMapper.java deleted file mode 100644 index d7741c73..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/mapper/MeetingMapper.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.weeth.domain.schedule.application.mapper; - -import com.weeth.domain.attendance.domain.entity.Session; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; -import com.weeth.domain.user.domain.entity.User; -import org.mapstruct.*; - -import java.util.Random; - -import static com.weeth.domain.schedule.application.dto.MeetingDTO.Info; -import static com.weeth.domain.schedule.application.dto.MeetingDTO.Response; - -@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE) -public interface MeetingMapper { - - @Mapping(target = "name", source = "user.name") - @Mapping(target = "code", ignore = true) - @Mapping(target = "type", expression = "java(Type.MEETING)") - Response to(Session session); - - Info toInfo(Session session); - - @Mapping(target = "name", source = "user.name") - @Mapping(target = "type", expression = "java(Type.MEETING)") - Response toAdminResponse(Session session); - - @BeanMapping(builder = @Builder(disableBuilder = true)) - @Mappings({ - @Mapping(target = "id", ignore = true), - @Mapping(target = "code", expression = "java( generateCode() )"), - @Mapping(target = "status", ignore = true), - @Mapping(target = "user", source = "user") - }) - Session from(ScheduleDTO.Save dto, User user); - - default Integer generateCode() { - return new Random().nextInt(9000) + 1000; - } -} diff --git a/src/main/java/com/weeth/domain/schedule/application/mapper/EventMapper.java b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/EventMapper.kt similarity index 100% rename from src/main/java/com/weeth/domain/schedule/application/mapper/EventMapper.java rename to src/main/kotlin/com/weeth/domain/schedule/application/mapper/EventMapper.kt diff --git a/src/main/java/com/weeth/domain/schedule/application/mapper/ScheduleMapper.java b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt similarity index 100% rename from src/main/java/com/weeth/domain/schedule/application/mapper/ScheduleMapper.java rename to src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt From 33360c88eabacab15a3024636ac5f0094dbaf9a5 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 02:50:43 +0900 Subject: [PATCH 16/62] =?UTF-8?q?refactor:=20Schedule=20mapper=20kotlin=20?= =?UTF-8?q?=EB=AC=B8=EB=B2=95=EC=9C=BC=EB=A1=9C=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/mapper/EventMapper.kt | 54 ++++++++----- .../application/mapper/ScheduleMapper.kt | 40 +++++++--- .../application/mapper/SessionMapper.kt | 77 +++++++++++++++++++ 3 files changed, 142 insertions(+), 29 deletions(-) create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/EventMapper.kt b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/EventMapper.kt index e3c18272..1821fa0c 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/EventMapper.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/EventMapper.kt @@ -1,23 +1,41 @@ -package com.weeth.domain.schedule.application.mapper; +package com.weeth.domain.schedule.application.mapper -import com.weeth.domain.schedule.application.dto.ScheduleDTO; -import com.weeth.domain.schedule.domain.entity.Event; -import com.weeth.domain.user.domain.entity.User; -import org.mapstruct.*; +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest +import com.weeth.domain.schedule.application.dto.response.EventResponse +import com.weeth.domain.schedule.domain.entity.Event +import com.weeth.domain.schedule.domain.entity.enums.Type +import com.weeth.domain.user.domain.entity.User +import org.springframework.stereotype.Component -import static com.weeth.domain.schedule.application.dto.EventDTO.Response; +@Component +class EventMapper { -@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE) -public interface EventMapper { + fun toResponse(event: Event): EventResponse = + EventResponse( + id = event.id, + title = event.title, + content = event.content, + location = event.location, + name = event.user?.name, + cardinal = event.cardinal, + type = Type.EVENT, + start = event.start, + end = event.end, + createdAt = event.createdAt, + modifiedAt = event.modifiedAt, + ) - @Mapping(target = "name", source = "event.user.name") - @Mapping(target = "type", expression = "java(Type.EVENT)") - Response to(Event event); - - @BeanMapping(builder = @Builder(disableBuilder = true)) - @Mappings({ - @Mapping(target = "id", ignore = true), - @Mapping(target = "user", source = "user") - }) - Event from(ScheduleDTO.Save dto, User user); + fun toEntity( + request: ScheduleSaveRequest, + user: User, + ): Event = + Event.create( + title = request.title, + content = request.content, + location = request.location, + cardinal = request.cardinal!!, + start = request.start!!, + end = request.end!!, + user = user, + ) } diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt index e316c216..b907c505 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt @@ -1,16 +1,34 @@ -package com.weeth.domain.schedule.application.mapper; +package com.weeth.domain.schedule.application.mapper -import com.weeth.domain.attendance.domain.entity.Session; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; -import com.weeth.domain.schedule.domain.entity.Event; -import org.mapstruct.Mapper; -import org.mapstruct.MappingConstants; -import org.mapstruct.ReportingPolicy; +import com.weeth.domain.attendance.domain.entity.Session +import com.weeth.domain.schedule.application.dto.response.ScheduleResponse +import com.weeth.domain.schedule.domain.entity.Event +import org.springframework.stereotype.Component -@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE) -public interface ScheduleMapper { +@Component +class ScheduleMapper { - ScheduleDTO.Response toScheduleDTO(Event event, Boolean isMeeting); + fun toResponse( + event: Event, + isMeeting: Boolean, + ): ScheduleResponse = + ScheduleResponse( + id = event.id, + title = event.title, + start = event.start, + end = event.end, + isMeeting = isMeeting, + ) - ScheduleDTO.Response toScheduleDTO(Session session, Boolean isMeeting); + fun toResponse( + session: Session, + isMeeting: Boolean, + ): ScheduleResponse = + ScheduleResponse( + id = session.id, + title = session.title, + start = session.start, + end = session.end, + isMeeting = isMeeting, + ) } diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt new file mode 100644 index 00000000..d45d98e7 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt @@ -0,0 +1,77 @@ +package com.weeth.domain.schedule.application.mapper + +import com.weeth.domain.attendance.domain.entity.Session +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest +import com.weeth.domain.schedule.application.dto.response.SessionInfoResponse +import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse +import com.weeth.domain.schedule.application.dto.response.SessionResponse +import com.weeth.domain.schedule.domain.entity.enums.Type +import com.weeth.domain.user.domain.entity.User +import org.springframework.stereotype.Component + +@Component +class SessionMapper { + + fun toResponse(session: Session): SessionResponse = + SessionResponse( + id = session.id, + title = session.title, + content = session.content, + location = session.location, + name = session.user?.name, + cardinal = session.cardinal, + type = Type.MEETING, + code = null, + start = session.start, + end = session.end, + createdAt = session.createdAt, + modifiedAt = session.modifiedAt, + ) + + fun toAdminResponse(session: Session): SessionResponse = + SessionResponse( + id = session.id, + title = session.title, + content = session.content, + location = session.location, + name = session.user?.name, + cardinal = session.cardinal, + type = Type.MEETING, + code = session.code, + start = session.start, + end = session.end, + createdAt = session.createdAt, + modifiedAt = session.modifiedAt, + ) + + fun toInfo(session: Session): SessionInfoResponse = + SessionInfoResponse( + id = session.id, + cardinal = session.cardinal, + title = session.title, + start = session.start, + ) + + fun toInfos( + thisWeek: Session?, + sessions: List, + ): SessionInfosResponse = + SessionInfosResponse( + thisWeek = thisWeek?.let { toInfo(it) }, + meetings = sessions.map { toInfo(it) }, + ) + + fun toEntity( + request: ScheduleSaveRequest, + user: User, + ): Session = + Session.create( + title = request.title, + content = request.content, + location = request.location, + cardinal = request.cardinal!!, + start = request.start!!, + end = request.end!!, + user = user, + ) +} From e43638206e95f5fc304ea2bf2673d4c28a97439b Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 02:57:56 +0900 Subject: [PATCH 17/62] =?UTF-8?q?refactor:=20Schedule=20validator=20java?= =?UTF-8?q?=20->=20kotlin=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD,=20java=20dto=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../schedule/application/dto/EventDTO.java | 24 ---------- .../schedule/application/dto/MeetingDTO.java | 41 ----------------- .../schedule/application/dto/ScheduleDTO.java | 45 ------------------- .../validator/ScheduleTimeCheckValidator.kt} | 0 4 files changed, 110 deletions(-) delete mode 100644 src/main/java/com/weeth/domain/schedule/application/dto/EventDTO.java delete mode 100644 src/main/java/com/weeth/domain/schedule/application/dto/MeetingDTO.java delete mode 100644 src/main/java/com/weeth/domain/schedule/application/dto/ScheduleDTO.java rename src/main/{java/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.java => kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt} (100%) diff --git a/src/main/java/com/weeth/domain/schedule/application/dto/EventDTO.java b/src/main/java/com/weeth/domain/schedule/application/dto/EventDTO.java deleted file mode 100644 index ee949d3a..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/dto/EventDTO.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.weeth.domain.schedule.application.dto; - -import com.weeth.domain.schedule.domain.entity.enums.Type; - -import java.time.LocalDateTime; - -public class EventDTO { - - public record Response( - Long id, - String title, - String content, - String location, - String requiredItem, - String name, - Integer cardinal, - Type type, - LocalDateTime start, - LocalDateTime end, - LocalDateTime createdAt, - LocalDateTime modifiedAt - ) {} -} - diff --git a/src/main/java/com/weeth/domain/schedule/application/dto/MeetingDTO.java b/src/main/java/com/weeth/domain/schedule/application/dto/MeetingDTO.java deleted file mode 100644 index 09b89017..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/dto/MeetingDTO.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.weeth.domain.schedule.application.dto; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.weeth.domain.schedule.domain.entity.enums.Type; - -import java.time.LocalDateTime; -import java.util.List; - -public class MeetingDTO { - - @JsonInclude(JsonInclude.Include.NON_NULL) - public record Response( - Long id, - String title, - String content, - String location, - String requiredItem, - String name, - Integer cardinal, - Type type, - Integer code, - LocalDateTime start, - LocalDateTime end, - LocalDateTime createdAt, - LocalDateTime modifiedAt - ) {} - - public record Info( - Long id, - Integer cardinal, - String title, - LocalDateTime start - ) {} - - public record Infos( - Info thisWeek, - List meetings - ) {} - - -} diff --git a/src/main/java/com/weeth/domain/schedule/application/dto/ScheduleDTO.java b/src/main/java/com/weeth/domain/schedule/application/dto/ScheduleDTO.java deleted file mode 100644 index 0aecfa05..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/dto/ScheduleDTO.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.weeth.domain.schedule.application.dto; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import com.weeth.domain.schedule.domain.entity.enums.Type; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -public class ScheduleDTO { - - public record Response( - Long id, - String title, - LocalDateTime start, - LocalDateTime end, - Boolean isMeeting - ) {} - - public record Time( - @NotNull @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start, - @NotNull @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime end - ) {} - - public record Save( - @NotBlank String title, - @NotBlank String content, - @NotBlank String location, - String requiredItem, - @NotNull Type type, - @NotNull Integer cardinal, - @NotNull @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start, - @NotNull @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime end - ) {} - - public record Update( - @NotBlank String title, - @NotBlank String content, - @NotBlank String location, - String requiredItem, - @NotNull Type type, - @NotNull @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start, - @NotNull @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime end - ) {} -} diff --git a/src/main/java/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.java b/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt similarity index 100% rename from src/main/java/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.java rename to src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt From 8db119be6da2c736505a73725cbd29712680005b Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 02:59:13 +0900 Subject: [PATCH 18/62] =?UTF-8?q?refactor:=20Schedule=20validator=20kotlin?= =?UTF-8?q?=20=EB=AC=B8=EB=B2=95=EC=9C=BC=EB=A1=9C=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../validator/ScheduleTimeCheckValidator.kt | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt b/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt index 62668e4b..94a18c9d 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt @@ -1,19 +1,14 @@ -package com.weeth.domain.schedule.application.validator; +package com.weeth.domain.schedule.application.validator -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import com.weeth.domain.schedule.application.annotation.ScheduleTimeCheck; -import com.weeth.domain.schedule.application.dto.ScheduleDTO.Time; +import com.weeth.domain.schedule.application.annotation.ScheduleTimeCheck +import com.weeth.domain.schedule.application.dto.request.ScheduleTimeRequest +import jakarta.validation.ConstraintValidator +import jakarta.validation.ConstraintValidatorContext -public class ScheduleTimeCheckValidator implements ConstraintValidator { +class ScheduleTimeCheckValidator : ConstraintValidator { - @Override - public void initialize(ScheduleTimeCheck constraintAnnotation) { - ConstraintValidator.super.initialize(constraintAnnotation); - } - - @Override - public boolean isValid(Time time, ConstraintValidatorContext context) { - return time.start().isBefore(time.end().plusMinutes(1)); - } -} \ No newline at end of file + override fun isValid( + time: ScheduleTimeRequest, + context: ConstraintValidatorContext, + ): Boolean = time.start!!.isBefore(time.end!!.plusMinutes(1)) +} From e2fd22ab27b9fe43c23643ac701e4bd3d56db89f Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 03:11:18 +0900 Subject: [PATCH 19/62] =?UTF-8?q?refactor:=20Schedule=20UseCase/Service=20?= =?UTF-8?q?Kotlin=20DTO=20=EC=B0=B8=EC=A1=B0=EB=A1=9C=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/EventUseCase.java | 12 +++---- .../application/usecase/EventUseCaseImpl.java | 18 +++++------ .../application/usecase/MeetingUseCase.java | 19 +++++------- .../usecase/MeetingUseCaseImpl.java | 31 +++++++++---------- .../application/usecase/ScheduleUseCase.java | 8 ++--- .../usecase/ScheduleUseCaseImpl.java | 29 +++++++++-------- .../domain/service/EventGetService.java | 10 +++--- .../domain/service/EventUpdateService.java | 6 ++-- .../domain/service/MeetingGetService.java | 10 +++--- .../domain/service/MeetingUpdateService.java | 6 ++-- 10 files changed, 71 insertions(+), 78 deletions(-) diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCase.java b/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCase.java index e0227af7..eafd896a 100644 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCase.java +++ b/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCase.java @@ -1,16 +1,16 @@ package com.weeth.domain.schedule.application.usecase; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; - -import static com.weeth.domain.schedule.application.dto.EventDTO.*; +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest; +import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; +import com.weeth.domain.schedule.application.dto.response.EventResponse; public interface EventUseCase { - Response find(Long eventId); + EventResponse find(Long eventId); - void save(ScheduleDTO.Save dto, Long userId); + void save(ScheduleSaveRequest dto, Long userId); - void update(Long eventId, ScheduleDTO.Update dto, Long userId); + void update(Long eventId, ScheduleUpdateRequest dto, Long userId); void delete(Long eventId); } diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCaseImpl.java b/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCaseImpl.java index 62ada147..d6e18883 100644 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCaseImpl.java +++ b/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCaseImpl.java @@ -1,6 +1,8 @@ package com.weeth.domain.schedule.application.usecase; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest; +import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; +import com.weeth.domain.schedule.application.dto.response.EventResponse; import com.weeth.domain.schedule.application.mapper.EventMapper; import com.weeth.domain.schedule.domain.entity.Event; import com.weeth.domain.schedule.domain.service.EventDeleteService; @@ -14,8 +16,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static com.weeth.domain.schedule.application.dto.EventDTO.Response; - @Service @RequiredArgsConstructor public class EventUseCaseImpl implements EventUseCase { @@ -29,22 +29,22 @@ public class EventUseCaseImpl implements EventUseCase { private final EventMapper mapper; @Override - public Response find(Long eventId) { - return mapper.to(eventGetService.find(eventId)); + public EventResponse find(Long eventId) { + return mapper.toResponse(eventGetService.find(eventId)); } @Override @Transactional - public void save(ScheduleDTO.Save dto, Long userId) { + public void save(ScheduleSaveRequest dto, Long userId) { User user = userGetService.find(userId); - cardinalGetService.findByUserSide(dto.cardinal()); + cardinalGetService.findByUserSide(dto.getCardinal()); - eventSaveService.save(mapper.from(dto, user)); + eventSaveService.save(mapper.toEntity(dto, user)); } @Override @Transactional - public void update(Long eventId, ScheduleDTO.Update dto, Long userId) { + public void update(Long eventId, ScheduleUpdateRequest dto, Long userId) { User user = userGetService.find(userId); Event event = eventGetService.find(eventId); eventUpdateService.update(event, dto, user); diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCase.java b/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCase.java index 857de980..d0d76c94 100644 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCase.java +++ b/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCase.java @@ -1,22 +1,19 @@ package com.weeth.domain.schedule.application.usecase; -import com.weeth.domain.schedule.application.dto.MeetingDTO; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; - -import java.util.List; - -import static com.weeth.domain.schedule.application.dto.MeetingDTO.Info; -import static com.weeth.domain.schedule.application.dto.MeetingDTO.Response; +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest; +import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; +import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse; +import com.weeth.domain.schedule.application.dto.response.SessionResponse; public interface MeetingUseCase { - Response find(Long userId, Long eventId); + SessionResponse find(Long userId, Long eventId); - MeetingDTO.Infos find(Integer cardinal); + SessionInfosResponse find(Integer cardinal); - void save(ScheduleDTO.Save dto, Long userId); + void save(ScheduleSaveRequest dto, Long userId); - void update(ScheduleDTO.Update dto, Long userId, Long meetingId); + void update(ScheduleUpdateRequest dto, Long userId, Long meetingId); void delete(Long meetingId); } diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java b/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java index 97351d1c..e095258b 100644 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java +++ b/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java @@ -8,9 +8,11 @@ import com.weeth.domain.attendance.domain.service.AttendanceGetService; import com.weeth.domain.attendance.domain.service.AttendanceSaveService; import com.weeth.domain.attendance.domain.service.AttendanceUpdateService; -import com.weeth.domain.schedule.application.dto.MeetingDTO; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; -import com.weeth.domain.schedule.application.mapper.MeetingMapper; +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest; +import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; +import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse; +import com.weeth.domain.schedule.application.dto.response.SessionResponse; +import com.weeth.domain.schedule.application.mapper.SessionMapper; import com.weeth.domain.schedule.domain.service.MeetingDeleteService; import com.weeth.domain.schedule.domain.service.MeetingGetService; import com.weeth.domain.schedule.domain.service.MeetingSaveService; @@ -31,16 +33,13 @@ import java.util.Comparator; import java.util.List; -import static com.weeth.domain.schedule.application.dto.MeetingDTO.Info; -import static com.weeth.domain.schedule.application.dto.MeetingDTO.Response; - @Slf4j @Service @RequiredArgsConstructor public class MeetingUseCaseImpl implements MeetingUseCase { private final MeetingGetService meetingGetService; - private final MeetingMapper mapper; + private final SessionMapper mapper; private final MeetingSaveService meetingSaveService; private final UserGetService userGetService; private final MeetingUpdateService meetingUpdateService; @@ -55,7 +54,7 @@ public class MeetingUseCaseImpl implements MeetingUseCase { private EntityManager em; @Override - public Response find(Long userId, Long sessionId) { + public SessionResponse find(Long userId, Long sessionId) { User user = userGetService.find(userId); Session session = meetingGetService.find(sessionId); @@ -63,11 +62,11 @@ public Response find(Long userId, Long sessionId) { return mapper.toAdminResponse(session); } - return mapper.to(session); + return mapper.toResponse(session); } @Override - public MeetingDTO.Infos find(Integer cardinal) { + public SessionInfosResponse find(Integer cardinal) { List sessions; if (cardinal == null) { @@ -79,20 +78,18 @@ public MeetingDTO.Infos find(Integer cardinal) { Session thisWeek = findThisWeek(sessions); List sorted = sortSessions(sessions); - return new MeetingDTO.Infos( - thisWeek != null ? mapper.toInfo(thisWeek) : null, - sorted.stream().map(mapper::toInfo).toList()); + return mapper.toInfos(thisWeek, sorted); } @Override @Transactional - public void save(ScheduleDTO.Save dto, Long userId) { + public void save(ScheduleSaveRequest dto, Long userId) { User user = userGetService.find(userId); - Cardinal cardinal = cardinalGetService.findByUserSide(dto.cardinal()); + Cardinal cardinal = cardinalGetService.findByUserSide(dto.getCardinal()); List userList = userGetService.findAllByCardinal(cardinal); - Session session = mapper.from(dto, user); + Session session = mapper.toEntity(dto, user); meetingSaveService.save(session); attendanceSaveService.saveAll(userList, session); @@ -100,7 +97,7 @@ public void save(ScheduleDTO.Save dto, Long userId) { @Override @Transactional - public void update(ScheduleDTO.Update dto, Long userId, Long sessionId) { + public void update(ScheduleUpdateRequest dto, Long userId, Long sessionId) { Session session = meetingGetService.find(sessionId); User user = userGetService.find(userId); meetingUpdateService.update(dto, user, session); diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCase.java b/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCase.java index 4676a026..9721cb6a 100644 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCase.java +++ b/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCase.java @@ -1,15 +1,15 @@ package com.weeth.domain.schedule.application.usecase; +import com.weeth.domain.schedule.application.dto.response.ScheduleResponse; + import java.time.LocalDateTime; import java.util.List; import java.util.Map; -import static com.weeth.domain.schedule.application.dto.ScheduleDTO.Response; - public interface ScheduleUseCase { - List findByMonthly(LocalDateTime start, LocalDateTime end); + List findByMonthly(LocalDateTime start, LocalDateTime end); - Map> findByYearly(Integer year, Integer semester); + Map> findByYearly(Integer year, Integer semester); } diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImpl.java b/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImpl.java index c7818d1d..958574a7 100644 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImpl.java +++ b/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImpl.java @@ -1,5 +1,6 @@ package com.weeth.domain.schedule.application.usecase; +import com.weeth.domain.schedule.application.dto.response.ScheduleResponse; import com.weeth.domain.schedule.domain.service.EventGetService; import com.weeth.domain.schedule.domain.service.MeetingGetService; import com.weeth.domain.user.domain.entity.Cardinal; @@ -13,8 +14,6 @@ import java.util.stream.IntStream; import java.util.stream.Stream; -import static com.weeth.domain.schedule.application.dto.ScheduleDTO.Response; - @Service @RequiredArgsConstructor public class ScheduleUseCaseImpl implements ScheduleUseCase { @@ -24,32 +23,32 @@ public class ScheduleUseCaseImpl implements ScheduleUseCase { private final CardinalGetService cardinalGetService; @Override - public List findByMonthly(LocalDateTime start, LocalDateTime end) { - List events = eventGetService.find(start, end); - List meetings = meetingGetService.find(start, end); + public List findByMonthly(LocalDateTime start, LocalDateTime end) { + List events = eventGetService.find(start, end); + List meetings = meetingGetService.find(start, end); return Stream.of(events, meetings) .flatMap(Collection::stream) - .sorted(Comparator.comparing(Response::start)) + .sorted(Comparator.comparing(ScheduleResponse::getStart)) .toList(); } @Override - public Map> findByYearly(Integer year, Integer semester) { + public Map> findByYearly(Integer year, Integer semester) { Cardinal cardinal = cardinalGetService.find(year, semester); - List events = eventGetService.find(cardinal.getCardinalNumber()); - List meetings = meetingGetService.findByCardinal(cardinal.getCardinalNumber()); + List events = eventGetService.find(cardinal.getCardinalNumber()); + List meetings = meetingGetService.findByCardinal(cardinal.getCardinalNumber()); return Stream.of(events, meetings) - .flatMap(Collection::stream) // 병합 - .sorted(Comparator.comparing(Response::start)) // 스케줄 시작 시간으로 정렬 + .flatMap(Collection::stream) + .sorted(Comparator.comparing(ScheduleResponse::getStart)) .flatMap(schedule -> { - List> monthEventPairs = new ArrayList<>(); + List> monthEventPairs = new ArrayList<>(); - int left = schedule.start().getMonthValue(); - int right = schedule.end().getMonthValue() + 1; - IntStream.range(left, right) // 기간 내 포함된 달 계산 + int left = schedule.getStart().getMonthValue(); + int right = schedule.getEnd().getMonthValue() + 1; + IntStream.range(left, right) .forEach(month -> monthEventPairs.add( new AbstractMap.SimpleEntry<>(month, schedule)) ); diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/EventGetService.java b/src/main/java/com/weeth/domain/schedule/domain/service/EventGetService.java index 71546635..3560e4ff 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/service/EventGetService.java +++ b/src/main/java/com/weeth/domain/schedule/domain/service/EventGetService.java @@ -1,6 +1,6 @@ package com.weeth.domain.schedule.domain.service; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; +import com.weeth.domain.schedule.application.dto.response.ScheduleResponse; import com.weeth.domain.schedule.application.mapper.ScheduleMapper; import com.weeth.domain.schedule.domain.entity.Event; import com.weeth.domain.schedule.domain.repository.EventRepository; @@ -23,15 +23,15 @@ public Event find(Long eventId) { .orElseThrow(EventNotFoundException::new); } - public List find(LocalDateTime start, LocalDateTime end) { + public List find(LocalDateTime start, LocalDateTime end) { return eventRepository.findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start).stream() - .map(event -> mapper.toScheduleDTO(event, false)) + .map(event -> mapper.toResponse(event, false)) .toList(); } - public List find(Integer cardinal) { + public List find(Integer cardinal) { return eventRepository.findAllByCardinal(cardinal).stream() - .map(event -> mapper.toScheduleDTO(event, false)) + .map(event -> mapper.toResponse(event, false)) .toList(); } } diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java b/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java index 7d38bcea..842fb2f9 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java +++ b/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java @@ -1,7 +1,7 @@ package com.weeth.domain.schedule.domain.service; import jakarta.transaction.Transactional; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; +import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; import com.weeth.domain.schedule.domain.entity.Event; import com.weeth.domain.user.domain.entity.User; import lombok.RequiredArgsConstructor; @@ -12,7 +12,7 @@ @RequiredArgsConstructor public class EventUpdateService { - public void update(Event event, ScheduleDTO.Update dto, User user) { - event.update(dto.title(), dto.content(), dto.location(), dto.start(), dto.end(), user); + public void update(Event event, ScheduleUpdateRequest dto, User user) { + event.update(dto.getTitle(), dto.getContent(), dto.getLocation(), dto.getStart(), dto.getEnd(), user); } } diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java index 08148460..b0c1ea73 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java +++ b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java @@ -3,7 +3,7 @@ import com.weeth.domain.attendance.domain.entity.Session; import com.weeth.domain.attendance.domain.entity.enums.SessionStatus; import com.weeth.domain.attendance.domain.repository.SessionRepository; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; +import com.weeth.domain.schedule.application.dto.response.ScheduleResponse; import com.weeth.domain.schedule.application.mapper.ScheduleMapper; import com.weeth.domain.schedule.application.exception.MeetingNotFoundException; import lombok.RequiredArgsConstructor; @@ -24,9 +24,9 @@ public Session find(Long sessionId) { .orElseThrow(MeetingNotFoundException::new); } - public List find(LocalDateTime start, LocalDateTime end) { + public List find(LocalDateTime start, LocalDateTime end) { return sessionRepository.findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start).stream() - .map(session -> mapper.toScheduleDTO(session, true)) + .map(session -> mapper.toResponse(session, true)) .toList(); } @@ -42,9 +42,9 @@ public List findAll() { return sessionRepository.findAllByOrderByStartDesc(); } - public List findByCardinal(Integer cardinal) { + public List findByCardinal(Integer cardinal) { return sessionRepository.findAllByCardinal(cardinal).stream() - .map(session -> mapper.toScheduleDTO(session, true)) + .map(session -> mapper.toResponse(session, true)) .toList(); } diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java index 93985fdc..ee698f35 100644 --- a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java +++ b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java @@ -1,14 +1,14 @@ package com.weeth.domain.schedule.domain.service; import com.weeth.domain.attendance.domain.entity.Session; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; +import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; import com.weeth.domain.user.domain.entity.User; import org.springframework.stereotype.Service; @Service public class MeetingUpdateService { - public void update(ScheduleDTO.Update dto, User user, Session session) { - session.updateInfo(dto.title(), dto.content(), dto.location(), dto.start(), dto.end(), user); + public void update(ScheduleUpdateRequest dto, User user, Session session) { + session.updateInfo(dto.getTitle(), dto.getContent(), dto.getLocation(), dto.getStart(), dto.getEnd(), user); } } From f5a13a1d2204b8170dc12e53a6012a5ccc6e28c8 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 03:11:39 +0900 Subject: [PATCH 20/62] =?UTF-8?q?refactor:=20Schedule=20Controller=20Kotli?= =?UTF-8?q?n=20DTO=20=EC=B0=B8=EC=A1=B0=EB=A1=9C=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/EventAdminController.java | 11 ++++++----- .../schedule/presentation/EventController.java | 4 ++-- .../schedule/presentation/MeetingController.java | 6 +++--- .../presentation/ScheduleController.java | 16 +++++++++------- .../presentation/AttendanceAdminController.kt | 4 ++-- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java b/src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java index 2a8f2a99..eb11811f 100644 --- a/src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java +++ b/src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java @@ -4,7 +4,8 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import com.weeth.domain.schedule.application.dto.ScheduleDTO; +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest; +import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; import com.weeth.domain.schedule.application.exception.EventErrorCode; import com.weeth.domain.schedule.application.usecase.EventUseCase; import com.weeth.domain.schedule.application.usecase.MeetingUseCase; @@ -29,9 +30,9 @@ public class EventAdminController { @PostMapping @Operation(summary = "일정/정기모임 생성") - public CommonResponse save(@Valid @RequestBody ScheduleDTO.Save dto, + public CommonResponse save(@Valid @RequestBody ScheduleSaveRequest dto, @Parameter(hidden = true) @CurrentUser Long userId) { - if (dto.type() == Type.EVENT) { + if (dto.getType() == Type.EVENT) { eventUseCase.save(dto, userId); } else { meetingUseCase.save(dto, userId); @@ -42,9 +43,9 @@ public CommonResponse save(@Valid @RequestBody ScheduleDTO.Save dto, @PatchMapping("/{eventId}") @Operation(summary = "일정 수정 (type은 변경할 수 없게 해주세요.)") - public CommonResponse update(@PathVariable Long eventId, @Valid @RequestBody ScheduleDTO.Update dto, + public CommonResponse update(@PathVariable Long eventId, @Valid @RequestBody ScheduleUpdateRequest dto, @Parameter(hidden = true) @CurrentUser Long userId) { - if (dto.type() == Type.EVENT) { + if (dto.getType() == Type.EVENT) { eventUseCase.update(eventId, dto, userId); } else { meetingUseCase.update(dto, userId, eventId); diff --git a/src/main/java/com/weeth/domain/schedule/presentation/EventController.java b/src/main/java/com/weeth/domain/schedule/presentation/EventController.java index d94165e9..2413105c 100644 --- a/src/main/java/com/weeth/domain/schedule/presentation/EventController.java +++ b/src/main/java/com/weeth/domain/schedule/presentation/EventController.java @@ -2,6 +2,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import com.weeth.domain.schedule.application.dto.response.EventResponse; import com.weeth.domain.schedule.application.exception.EventErrorCode; import com.weeth.domain.schedule.application.usecase.EventUseCase; import com.weeth.global.common.exception.ApiErrorCodeExample; @@ -12,7 +13,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import static com.weeth.domain.schedule.application.dto.EventDTO.Response; import static com.weeth.domain.schedule.presentation.ScheduleResponseCode.EVENT_FIND_SUCCESS; @Tag(name = "EVENT", description = "일정 API") @@ -26,7 +26,7 @@ public class EventController { @GetMapping("/{eventId}") @Operation(summary="일정 상세 조회") - public CommonResponse find(@PathVariable Long eventId) { + public CommonResponse find(@PathVariable Long eventId) { return CommonResponse.success(EVENT_FIND_SUCCESS, eventUseCase.find(eventId)); } diff --git a/src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java b/src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java index 3af43758..a4a89363 100644 --- a/src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java +++ b/src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java @@ -3,7 +3,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import com.weeth.domain.schedule.application.dto.MeetingDTO; +import com.weeth.domain.schedule.application.dto.response.SessionResponse; import com.weeth.domain.schedule.application.exception.MeetingErrorCode; import com.weeth.domain.schedule.application.usecase.MeetingUseCase; import com.weeth.global.auth.annotation.CurrentUser; @@ -28,8 +28,8 @@ public class MeetingController { @GetMapping("/{meetingId}") @Operation(summary="정기모임 상세 조회") - public CommonResponse find(@Parameter(hidden = true) @CurrentUser Long userId, - @PathVariable Long meetingId) { + public CommonResponse find(@Parameter(hidden = true) @CurrentUser Long userId, + @PathVariable Long meetingId) { return CommonResponse.success(MEETING_FIND_SUCCESS, meetingUseCase.find(userId, meetingId)); } } diff --git a/src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java b/src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java index 7e3203c5..6a6a758f 100644 --- a/src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java +++ b/src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java @@ -2,6 +2,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import com.weeth.domain.schedule.application.dto.response.ScheduleResponse; import com.weeth.domain.schedule.application.exception.EventErrorCode; import com.weeth.domain.schedule.application.exception.MeetingErrorCode; import com.weeth.domain.schedule.application.usecase.ScheduleUseCase; @@ -18,7 +19,6 @@ import java.util.List; import java.util.Map; -import static com.weeth.domain.schedule.application.dto.ScheduleDTO.Response; import static com.weeth.domain.schedule.presentation.ScheduleResponseCode.SCHEDULE_MONTHLY_FIND_SUCCESS; import static com.weeth.domain.schedule.presentation.ScheduleResponseCode.SCHEDULE_YEARLY_FIND_SUCCESS; @@ -33,15 +33,17 @@ public class ScheduleController { @GetMapping("/monthly") @Operation(summary="월별 일정 조회") - public CommonResponse> findByMonthly(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start, - @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime end) { - return CommonResponse.success(SCHEDULE_MONTHLY_FIND_SUCCESS,scheduleUseCase.findByMonthly(start, end)); + public CommonResponse> findByMonthly( + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start, + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime end) { + return CommonResponse.success(SCHEDULE_MONTHLY_FIND_SUCCESS, scheduleUseCase.findByMonthly(start, end)); } @GetMapping("/yearly") @Operation(summary="연도별 일정 조회") - public CommonResponse>> findByYearly(@RequestParam Integer year, - @RequestParam Integer semester) { - return CommonResponse.success(SCHEDULE_YEARLY_FIND_SUCCESS,scheduleUseCase.findByYearly(year, semester)); + public CommonResponse>> findByYearly( + @RequestParam Integer year, + @RequestParam Integer semester) { + return CommonResponse.success(SCHEDULE_YEARLY_FIND_SUCCESS, scheduleUseCase.findByYearly(year, semester)); } } diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt index 4e8ca2a2..2925ea2e 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt @@ -6,7 +6,7 @@ import com.weeth.domain.attendance.application.exception.AttendanceErrorCode import com.weeth.domain.attendance.application.usecase.command.CloseAttendanceUseCase import com.weeth.domain.attendance.application.usecase.command.UpdateAttendanceStatusUseCase import com.weeth.domain.attendance.application.usecase.query.GetAttendanceQueryService -import com.weeth.domain.schedule.application.dto.MeetingDTO +import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse import com.weeth.domain.schedule.application.usecase.MeetingUseCase import com.weeth.global.common.exception.ApiErrorCodeExample import com.weeth.global.common.response.CommonResponse @@ -46,7 +46,7 @@ class AttendanceAdminController( @Operation(summary = "정기모임 조회") fun getMeetings( @RequestParam(required = false) cardinal: Int?, - ): CommonResponse { + ): CommonResponse { val response = meetingUseCase.find(cardinal) return CommonResponse.success(AttendanceResponseCode.MEETING_FIND_SUCCESS, response) } From 1baa6c9c09ab881ec53e4d8a57511788136fbb9a Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 03:12:05 +0900 Subject: [PATCH 21/62] =?UTF-8?q?refactor:=20Schedule=20mapper/validator?= =?UTF-8?q?=20Kotlin=20DTO=20=EC=B0=B8=EC=A1=B0=EB=A1=9C=20=EC=A0=84?= =?UTF-8?q?=ED=99=98=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/mapper/EventMapper.kt | 7 +-- .../application/mapper/ScheduleMapper.kt | 1 - .../application/mapper/SessionMapper.kt | 7 +-- .../validator/ScheduleTimeCheckValidator.kt | 3 +- .../usecase/MeetingUseCaseImplTest.kt | 57 ++++++++++--------- .../usecase/ScheduleUseCaseImplTest.kt | 10 ++-- 6 files changed, 41 insertions(+), 44 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/EventMapper.kt b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/EventMapper.kt index 1821fa0c..a1caa781 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/EventMapper.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/EventMapper.kt @@ -9,7 +9,6 @@ import org.springframework.stereotype.Component @Component class EventMapper { - fun toResponse(event: Event): EventResponse = EventResponse( id = event.id, @@ -33,9 +32,9 @@ class EventMapper { title = request.title, content = request.content, location = request.location, - cardinal = request.cardinal!!, - start = request.start!!, - end = request.end!!, + cardinal = request.cardinal, + start = request.start, + end = request.end, user = user, ) } diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt index b907c505..b86d1774 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt @@ -7,7 +7,6 @@ import org.springframework.stereotype.Component @Component class ScheduleMapper { - fun toResponse( event: Event, isMeeting: Boolean, diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt index d45d98e7..96b5bbf8 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt @@ -11,7 +11,6 @@ import org.springframework.stereotype.Component @Component class SessionMapper { - fun toResponse(session: Session): SessionResponse = SessionResponse( id = session.id, @@ -69,9 +68,9 @@ class SessionMapper { title = request.title, content = request.content, location = request.location, - cardinal = request.cardinal!!, - start = request.start!!, - end = request.end!!, + cardinal = request.cardinal, + start = request.start, + end = request.end, user = user, ) } diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt b/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt index 94a18c9d..e71e1785 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt @@ -6,9 +6,8 @@ import jakarta.validation.ConstraintValidator import jakarta.validation.ConstraintValidatorContext class ScheduleTimeCheckValidator : ConstraintValidator { - override fun isValid( time: ScheduleTimeRequest, context: ConstraintValidatorContext, - ): Boolean = time.start!!.isBefore(time.end!!.plusMinutes(1)) + ): Boolean = time.start.isBefore(time.end.plusMinutes(1)) } diff --git a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt index 0b2130c0..cab5e472 100644 --- a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt +++ b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt @@ -7,9 +7,11 @@ import com.weeth.domain.attendance.domain.service.AttendanceDeleteService import com.weeth.domain.attendance.domain.service.AttendanceGetService import com.weeth.domain.attendance.domain.service.AttendanceSaveService import com.weeth.domain.attendance.domain.service.AttendanceUpdateService -import com.weeth.domain.schedule.application.dto.MeetingDTO -import com.weeth.domain.schedule.application.dto.ScheduleDTO -import com.weeth.domain.schedule.application.mapper.MeetingMapper +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest +import com.weeth.domain.schedule.application.dto.response.SessionInfoResponse +import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse +import com.weeth.domain.schedule.application.dto.response.SessionResponse +import com.weeth.domain.schedule.application.mapper.SessionMapper import com.weeth.domain.schedule.domain.entity.enums.Type import com.weeth.domain.schedule.domain.service.MeetingDeleteService import com.weeth.domain.schedule.domain.service.MeetingGetService @@ -35,7 +37,7 @@ import java.time.LocalDateTime class MeetingUseCaseImplTest : DescribeSpec({ val meetingGetService = mockk() - val meetingMapper = mockk() + val sessionMapper = mockk() val meetingSaveService = mockk(relaxUnitFun = true) val userGetService = mockk() val meetingUpdateService = mockk(relaxUnitFun = true) @@ -50,7 +52,7 @@ class MeetingUseCaseImplTest : val useCase = MeetingUseCaseImpl( meetingGetService, - meetingMapper, + sessionMapper, meetingSaveService, userGetService, meetingUpdateService, @@ -69,7 +71,7 @@ class MeetingUseCaseImplTest : beforeTest { clearMocks( meetingGetService, - meetingMapper, + sessionMapper, meetingSaveService, userGetService, meetingUpdateService, @@ -94,29 +96,29 @@ class MeetingUseCaseImplTest : every { adminUser.role } returns Role.ADMIN every { userGetService.find(userId) } returns adminUser every { meetingGetService.find(sessionId) } returns session - val adminResponse = mockk() - every { meetingMapper.toAdminResponse(session) } returns adminResponse + val adminResponse = mockk() + every { sessionMapper.toAdminResponse(session) } returns adminResponse val result = useCase.find(userId, sessionId) result shouldBe adminResponse - verify { meetingMapper.toAdminResponse(session) } + verify { sessionMapper.toAdminResponse(session) } } } context("일반 유저일 때") { - it("to(session)으로 매핑한다 (코드 미노출)") { + it("toResponse(session)으로 매핑한다 (코드 미노출)") { val normalUser = mockk() every { normalUser.role } returns Role.USER every { userGetService.find(userId) } returns normalUser every { meetingGetService.find(sessionId) } returns session - val normalResponse = mockk() - every { meetingMapper.to(session) } returns normalResponse + val normalResponse = mockk() + every { sessionMapper.toResponse(session) } returns normalResponse val result = useCase.find(userId, sessionId) result shouldBe normalResponse - verify { meetingMapper.to(session) } + verify { sessionMapper.toResponse(session) } } } } @@ -138,17 +140,17 @@ class MeetingUseCaseImplTest : start = now.minusDays(14), end = now.minusDays(14).plusHours(2), ) - val thisWeekInfo = MeetingDTO.Info(1L, 1, "This Week", now) - val lastWeekInfo = MeetingDTO.Info(2L, 1, "Last Week", now.minusDays(14)) + val thisWeekInfo = SessionInfoResponse(1L, 1, "This Week", now) + val lastWeekInfo = SessionInfoResponse(2L, 1, "Last Week", now.minusDays(14)) + val expectedInfos = SessionInfosResponse(thisWeekInfo, listOf(thisWeekInfo, lastWeekInfo)) every { meetingGetService.findMeetingByCardinal(1) } returns listOf(thisWeekSession, lastWeekSession) - every { meetingMapper.toInfo(thisWeekSession) } returns thisWeekInfo - every { meetingMapper.toInfo(lastWeekSession) } returns lastWeekInfo + every { sessionMapper.toInfos(thisWeekSession, any()) } returns expectedInfos val result = useCase.find(1) result.thisWeek shouldNotBe null - result.thisWeek.title() shouldBe "This Week" + result.thisWeek!!.title shouldBe "This Week" } } @@ -160,21 +162,20 @@ class MeetingUseCaseImplTest : val userList = listOf(mockk(), mockk(), mockk()) val session = ScheduleTestFixture.createSession() val dto = - ScheduleDTO.Save( - "Title", - "Content", - "Location", - null, - Type.MEETING, - 1, - LocalDateTime.of(2026, 3, 1, 10, 0), - LocalDateTime.of(2026, 3, 1, 12, 0), + ScheduleSaveRequest( + title = "Title", + content = "Content", + location = "Location", + type = Type.MEETING, + cardinal = 1, + start = LocalDateTime.of(2026, 3, 1, 10, 0), + end = LocalDateTime.of(2026, 3, 1, 12, 0), ) every { userGetService.find(userId) } returns user every { cardinalGetService.findByUserSide(1) } returns cardinal every { userGetService.findAllByCardinal(cardinal) } returns userList - every { meetingMapper.from(dto, user) } returns session + every { sessionMapper.toEntity(dto, user) } returns session useCase.save(dto, userId) diff --git a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt index bff8fe35..d0b3c8ee 100644 --- a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt +++ b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt @@ -1,6 +1,6 @@ package com.weeth.domain.schedule.application.usecase -import com.weeth.domain.schedule.application.dto.ScheduleDTO +import com.weeth.domain.schedule.application.dto.response.ScheduleResponse import com.weeth.domain.schedule.domain.service.EventGetService import com.weeth.domain.schedule.domain.service.MeetingGetService import com.weeth.domain.user.domain.service.CardinalGetService @@ -27,8 +27,8 @@ class ScheduleUseCaseImplTest : val end = LocalDateTime.of(2026, 3, 31, 23, 59) it("Event와 Meeting을 합쳐서 start 기준 오름차순 정렬한다") { - val event1 = ScheduleDTO.Response(1L, "Event", start.plusDays(2), start.plusDays(3), false) - val meeting1 = ScheduleDTO.Response(2L, "Meeting", start.plusDays(1), start.plusDays(1), true) + val event1 = ScheduleResponse(1L, "Event", start.plusDays(2), start.plusDays(3), false) + val meeting1 = ScheduleResponse(2L, "Meeting", start.plusDays(1), start.plusDays(1), true) every { eventGetService.find(start, end) } returns listOf(event1) every { meetingGetService.find(start, end) } returns listOf(meeting1) @@ -36,8 +36,8 @@ class ScheduleUseCaseImplTest : val result = useCase.findByMonthly(start, end) result.size shouldBe 2 - result[0].title() shouldBe "Meeting" - result[1].title() shouldBe "Event" + result[0].title shouldBe "Meeting" + result[1].title shouldBe "Event" } it("Event와 Meeting 모두 없으면 빈 리스트를 반환한다") { From 160e0de1c861a7081fdc2337d7055db35dbe3b97 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 10:25:35 +0900 Subject: [PATCH 22/62] =?UTF-8?q?refactor:=20Schedule=20event=20repository?= =?UTF-8?q?=20kotlin=20=EB=AC=B8=EB=B2=95=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/EventRepository.java | 14 -------------- .../schedule/domain/repository/EventRepository.kt | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/repository/EventRepository.java create mode 100644 src/main/kotlin/com/weeth/domain/schedule/domain/repository/EventRepository.kt diff --git a/src/main/java/com/weeth/domain/schedule/domain/repository/EventRepository.java b/src/main/java/com/weeth/domain/schedule/domain/repository/EventRepository.java deleted file mode 100644 index a281ed08..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/repository/EventRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.weeth.domain.schedule.domain.repository; - -import com.weeth.domain.schedule.domain.entity.Event; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.time.LocalDateTime; -import java.util.List; - -public interface EventRepository extends JpaRepository { - - List findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(LocalDateTime end, LocalDateTime start); - - List findAllByCardinal(int cardinal); -} diff --git a/src/main/kotlin/com/weeth/domain/schedule/domain/repository/EventRepository.kt b/src/main/kotlin/com/weeth/domain/schedule/domain/repository/EventRepository.kt new file mode 100644 index 00000000..a24b1804 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/domain/repository/EventRepository.kt @@ -0,0 +1,14 @@ +package com.weeth.domain.schedule.domain.repository + +import com.weeth.domain.schedule.domain.entity.Event +import org.springframework.data.jpa.repository.JpaRepository +import java.time.LocalDateTime + +interface EventRepository : JpaRepository { + fun findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc( + end: LocalDateTime, + start: LocalDateTime, + ): List + + fun findAllByCardinal(cardinal: Int): List +} From c7de6a178c30db0bf591cec9df621905bdb95676 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 10:27:31 +0900 Subject: [PATCH 23/62] =?UTF-8?q?refactor:=20SessionReader=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../attendance/domain/repository/SessionReader.kt | 11 +++++++++++ .../attendance/domain/repository/SessionRepository.kt | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt new file mode 100644 index 00000000..ad388199 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt @@ -0,0 +1,11 @@ +package com.weeth.domain.attendance.domain.repository + +import com.weeth.domain.attendance.domain.entity.Session +import java.time.LocalDateTime + +interface SessionReader { + fun findAllByStartBetween( + start: LocalDateTime, + end: LocalDateTime, + ): List +} diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt index e6ba28a7..12e700b2 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt @@ -5,7 +5,9 @@ import com.weeth.domain.attendance.domain.entity.enums.SessionStatus import org.springframework.data.jpa.repository.JpaRepository import java.time.LocalDateTime -interface SessionRepository : JpaRepository { +interface SessionRepository : + JpaRepository, + SessionReader { fun findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc( end: LocalDateTime, start: LocalDateTime, From 52804388445908aa0273a79cae19d7b2c9fc8609 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 10:30:52 +0900 Subject: [PATCH 24/62] =?UTF-8?q?refactor:=20User=20Attendance=20=EC=97=B0?= =?UTF-8?q?=EA=B4=80=EA=B4=80=EA=B3=84=20=EB=8B=A8=EC=88=9C=ED=99=94=20?= =?UTF-8?q?=EB=B0=8F=20N+1=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weeth/domain/user/domain/entity/User.java | 12 ------- .../attendance/domain/entity/Attendance.kt | 3 ++ .../domain/service/AttendanceSaveService.kt | 3 +- .../mapper/AttendanceMapperTest.kt | 15 ++++----- .../service/AttendanceSaveServiceTest.kt | 5 ++- .../fixture/AttendanceTestFixture.kt | 32 ------------------- .../usecase/UserManageUseCaseTest.kt | 2 -- 7 files changed, 13 insertions(+), 59 deletions(-) diff --git a/src/main/java/com/weeth/domain/user/domain/entity/User.java b/src/main/java/com/weeth/domain/user/domain/entity/User.java index 17a99a31..66e42da1 100644 --- a/src/main/java/com/weeth/domain/user/domain/entity/User.java +++ b/src/main/java/com/weeth/domain/user/domain/entity/User.java @@ -1,7 +1,6 @@ package com.weeth.domain.user.domain.entity; import jakarta.persistence.*; -import com.weeth.domain.attendance.domain.entity.Attendance; import com.weeth.domain.board.domain.entity.enums.Part; import com.weeth.domain.user.domain.entity.enums.Department; import com.weeth.domain.user.domain.entity.enums.Position; @@ -15,9 +14,6 @@ import lombok.experimental.SuperBuilder; import org.springframework.security.crypto.password.PasswordEncoder; -import java.util.ArrayList; -import java.util.List; - import static com.weeth.domain.user.application.dto.request.UserRequestDto.Update; @Entity @@ -71,9 +67,6 @@ public class User extends BaseEntity { private Integer warningCount; - @OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE, orphanRemoval = true) - private List attendances = new ArrayList<>(); - @PrePersist public void init() { status = Status.WAITING; @@ -128,12 +121,7 @@ public void reset(PasswordEncoder passwordEncoder) { this.password = passwordEncoder.encode(studentId); } - public void add(Attendance attendance) { - this.attendances.add(attendance); - } - public void initAttendance() { - this.attendances.clear(); this.attendanceCount = 0; this.absenceCount = 0; this.attendanceRate = 0; diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt index efc910f1..7b31cb87 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt @@ -13,6 +13,8 @@ import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.JoinColumn import jakarta.persistence.ManyToOne +import org.hibernate.annotations.OnDelete +import org.hibernate.annotations.OnDeleteAction @Entity class Attendance( @@ -21,6 +23,7 @@ class Attendance( val session: Session, @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") + @OnDelete(action = OnDeleteAction.CASCADE) val user: User, @Enumerated(EnumType.STRING) var status: AttendanceStatus = AttendanceStatus.PENDING, diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveService.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveService.kt index 77b242d6..ca870e7c 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveService.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveService.kt @@ -15,8 +15,7 @@ class AttendanceSaveService( sessions: List?, ) { sessions?.forEach { session -> - val attendance = attendanceRepository.save(Attendance.create(session, user)) - user.add(attendance) + attendanceRepository.save(Attendance.create(session, user)) } } diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt index 7555381e..3b814285 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt @@ -1,8 +1,7 @@ package com.weeth.domain.attendance.application.mapper import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUser -import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUserWithAttendances -import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createAdminUserWithAttendances +import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createAdminUser import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createAttendance import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createOneDaySession import com.weeth.domain.attendance.fixture.AttendanceTestFixture.enrichUserProfile @@ -24,8 +23,8 @@ class AttendanceMapperTest : it("사용자 + 당일 출석 객체를 MainResponse로 매핑한다") { val today = LocalDate.now() val meeting = createOneDaySession(today, 1, 1111, "Today") - val user = createActiveUserWithAttendances("이지훈", listOf(meeting)) - val attendance = user.attendances[0] + val user = createActiveUser("이지훈") + val attendance = createAttendance(meeting, user) val main = mapper.toSummaryResponse(user, attendance) @@ -52,8 +51,8 @@ class AttendanceMapperTest : it("일반 유저는 출석 코드가 null로 매핑된다") { val today = LocalDate.now() val meeting = createOneDaySession(today, 1, 1234, "Today") - val user = createActiveUserWithAttendances("일반유저", listOf(meeting)) - val attendance = user.attendances[0] + val user = createActiveUser("일반유저") + val attendance = createAttendance(meeting, user) val main = mapper.toSummaryResponse(user, attendance) @@ -67,8 +66,8 @@ class AttendanceMapperTest : val today = LocalDate.now() val expectedCode = 1234 val meeting = createOneDaySession(today, 1, expectedCode, "Today") - val adminUser = createAdminUserWithAttendances("관리자", listOf(meeting)) - val attendance = adminUser.attendances[0] + val adminUser = createAdminUser("관리자") + val attendance = createAttendance(meeting, adminUser) val main = mapper.toSummaryResponse(adminUser, attendance, isAdmin = true) diff --git a/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt b/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt index baa9ae4b..7831eb15 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt @@ -20,8 +20,8 @@ class AttendanceSaveServiceTest : val attendanceSaveService = AttendanceSaveService(attendanceRepository) describe("init") { - it("각 정기모임에 대한 Attendance 저장 후 user.add 호출") { - val user = mockk(relaxUnitFun = true) + it("각 정기모임에 대한 Attendance를 생성하여 저장한다") { + val user = createActiveUser("이지훈") val sessionFirst = createOneDaySession(LocalDate.now(), 1, 1234, "1차") val sessionSecond = createOneDaySession(LocalDate.now().plusDays(7), 1, 5678, "2차") @@ -30,7 +30,6 @@ class AttendanceSaveServiceTest : attendanceSaveService.init(user, listOf(sessionFirst, sessionSecond)) verify(exactly = 2) { attendanceRepository.save(any()) } - verify(exactly = 2) { user.add(any()) } } } diff --git a/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt b/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt index 99844904..ac14a79e 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt @@ -27,32 +27,6 @@ object AttendanceTestFixture { .role(Role.ADMIN) .build() - fun createActiveUserWithAttendances( - name: String, - sessions: List, - ): User { - val user = createActiveUser(name) - initAttendancesField(user) - sessions.forEach { session -> - val attendance = createAttendance(session, user) - user.add(attendance) - } - return user - } - - fun createAdminUserWithAttendances( - name: String, - sessions: List, - ): User { - val user = createAdminUser(name) - initAttendancesField(user) - sessions.forEach { session -> - val attendance = createAttendance(session, user) - user.add(attendance) - } - return user - } - fun createAttendance( session: Session, user: User, @@ -125,10 +99,4 @@ object AttendanceTestFixture { ReflectionTestUtils.setField(user, "department", department) ReflectionTestUtils.setField(user, "studentId", studentId) } - - private fun initAttendancesField(user: User) { - if (user.attendances == null) { - ReflectionTestUtils.setField(user, "attendances", mutableListOf()) - } - } } diff --git a/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt b/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt index b267a3e6..2a95830c 100644 --- a/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt +++ b/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt @@ -30,7 +30,6 @@ import io.mockk.mockk import io.mockk.verify import org.springframework.security.crypto.password.PasswordEncoder import java.time.LocalDateTime -import java.util.ArrayList class UserManageUseCaseTest : DescribeSpec({ @@ -201,7 +200,6 @@ class UserManageUseCaseTest : .id(1L) .name("aaa") .status(Status.ACTIVE) - .attendances(ArrayList()) .build() val nextCardinal = CardinalTestFixture.createCardinal(id = 1L, cardinalNumber = 4, year = 2020, semester = 2) val request = UserRequestDto.UserApplyOB(1L, 4) From 8440ae79560e8b168a908ce31b1b3c8bad848262 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 10:31:59 +0900 Subject: [PATCH 25/62] =?UTF-8?q?refactor:=20AttendanceRepository=20?= =?UTF-8?q?=EB=B0=B0=EC=B9=98=20=EC=A1=B0=ED=9A=8C=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=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 --- .../attendance/domain/repository/AttendanceRepository.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt index 61bec0c2..47e2b9eb 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt @@ -64,6 +64,11 @@ interface AttendanceRepository : JpaRepository { @Param("cardinal") cardinal: Int, ): List + @Query("SELECT a FROM Attendance a JOIN FETCH a.user WHERE a.session IN :sessions") + fun findAllBySessionIn( + @Param("sessions") sessions: List, + ): List + @Modifying @Query("DELETE FROM Attendance a WHERE a.session = :session") fun deleteAllBySession(session: Session) From a448a1ba1e51b85031be7827d7c94ff1b28e1556 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 11:42:38 +0900 Subject: [PATCH 26/62] =?UTF-8?q?refactor:=20AttendanceRepository=20delete?= =?UTF-8?q?AllBySession=20=EB=B2=8C=ED=81=AC=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/attendance/domain/repository/AttendanceRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt index 47e2b9eb..81b6a8cf 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt @@ -69,7 +69,7 @@ interface AttendanceRepository : JpaRepository { @Param("sessions") sessions: List, ): List - @Modifying + @Modifying(flushAutomatically = true, clearAutomatically = true) @Query("DELETE FROM Attendance a WHERE a.session = :session") fun deleteAllBySession(session: Session) } From cf7b18b6889be0d653089f9f47cb4515fc4577b0 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 11:44:56 +0900 Subject: [PATCH 27/62] =?UTF-8?q?refactor:=20session,=20evenet,=20attenden?= =?UTF-8?q?ce=20usecase=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/ManageAttendanceUseCase.kt | 96 ++++++++++++++++ .../usecase/command/ManageSessionUseCase.kt | 106 ++++++++++++++++++ .../usecase/command/ManageEventUseCase.kt | 50 +++++++++ 3 files changed, 252 insertions(+) create mode 100644 src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt create mode 100644 src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt new file mode 100644 index 00000000..b454291e --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt @@ -0,0 +1,96 @@ +package com.weeth.domain.attendance.application.usecase.command + +import com.weeth.domain.attendance.application.dto.request.UpdateAttendanceStatusRequest +import com.weeth.domain.attendance.application.exception.AttendanceCodeMismatchException +import com.weeth.domain.attendance.application.exception.AttendanceNotFoundException +import com.weeth.domain.attendance.domain.entity.Attendance +import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus +import com.weeth.domain.attendance.domain.entity.enums.SessionStatus +import com.weeth.domain.attendance.domain.repository.AttendanceRepository +import com.weeth.domain.attendance.domain.repository.SessionRepository +import com.weeth.domain.schedule.application.exception.MeetingNotFoundException +import com.weeth.domain.user.domain.entity.enums.Status +import com.weeth.domain.user.domain.service.UserGetService +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.time.LocalDate +import java.time.LocalDateTime + +@Service +class ManageAttendanceUseCase( + private val userGetService: UserGetService, + private val sessionRepository: SessionRepository, + private val attendanceRepository: AttendanceRepository, +) { + @Transactional + fun checkIn( + userId: Long, + code: Int, + ) { + val user = userGetService.find(userId) + val now = LocalDateTime.now() + val todayAttendance = + attendanceRepository.findCurrentByUserId(userId, now, now.plusMinutes(10)) + ?: throw AttendanceNotFoundException() + if (todayAttendance.isWrong(code)) { + throw AttendanceCodeMismatchException() + } + if (todayAttendance.status != AttendanceStatus.ATTEND) { + todayAttendance.attend() + user.attend() + } + } + + @Transactional + fun close( + now: LocalDate, + cardinal: Int, + ) { + val targetSession = + sessionRepository + .findAllByCardinalOrderByStartAsc(cardinal) + .firstOrNull { session -> session.start.toLocalDate().isEqual(now) && session.end.toLocalDate().isEqual(now) } + ?: throw MeetingNotFoundException() + val attendances = attendanceRepository.findAllBySessionAndUserStatus(targetSession, Status.ACTIVE) + closePendingAttendances(attendances) + } + + @Transactional + fun autoClose() { + val sessions = sessionRepository.findAllByStatusAndEndBeforeOrderByEndAsc(SessionStatus.OPEN, LocalDateTime.now()) + sessions.forEach { session -> + session.close() + val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) + closePendingAttendances(attendances) + } + } + + @Transactional + fun updateStatus(attendanceUpdates: List) { + attendanceUpdates.forEach { update -> + val attendance = + attendanceRepository.findByIdWithUser(update.attendanceId) + ?: throw AttendanceNotFoundException() + val user = attendance.user + val newStatus = AttendanceStatus.valueOf(update.status) + if (newStatus == AttendanceStatus.ABSENT) { + attendance.close() + user.removeAttend() + user.absent() + } else { + attendance.attend() + user.removeAbsent() + user.attend() + } + } + } + + private fun closePendingAttendances(attendances: List) { + attendances + .filter { it.isPending() } + .forEach { attendance -> + attendance.close() + attendance.user.absent() + } + } +} diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt new file mode 100644 index 00000000..dd9ebe81 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt @@ -0,0 +1,106 @@ +package com.weeth.domain.attendance.application.usecase.command + +import com.weeth.domain.attendance.domain.entity.Attendance +import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus +import com.weeth.domain.attendance.domain.repository.AttendanceRepository +import com.weeth.domain.attendance.domain.repository.SessionRepository +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest +import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest +import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse +import com.weeth.domain.schedule.application.dto.response.SessionResponse +import com.weeth.domain.schedule.application.exception.MeetingNotFoundException +import com.weeth.domain.schedule.application.mapper.SessionMapper +import com.weeth.domain.user.domain.entity.enums.Role +import com.weeth.domain.user.domain.entity.enums.Status +import com.weeth.domain.user.domain.service.CardinalGetService +import com.weeth.domain.user.domain.service.UserGetService +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.time.DayOfWeek +import java.time.LocalDate +import java.time.temporal.TemporalAdjusters + +@Service +class ManageSessionUseCase( + private val sessionRepository: SessionRepository, + private val attendanceRepository: AttendanceRepository, + private val userGetService: UserGetService, + private val cardinalGetService: CardinalGetService, + private val sessionMapper: SessionMapper, +) { + @Transactional + fun create( + request: ScheduleSaveRequest, + userId: Long, + ) { + val user = userGetService.find(userId) + val cardinal = cardinalGetService.findByUserSide(request.cardinal) + val users = userGetService.findAllByCardinal(cardinal) + val session = sessionMapper.toEntity(request, user) + sessionRepository.save(session) + attendanceRepository.saveAll(users.map { Attendance.create(session, it) }) + } + + @Transactional + fun update( + sessionId: Long, + request: ScheduleUpdateRequest, + userId: Long, + ) { + val session = sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException() } + val user = userGetService.find(userId) + session.updateInfo(request.title, request.content, request.location, request.start, request.end, user) + } + + @Transactional + fun delete(sessionId: Long) { + val session = sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException() } + val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) + attendances.forEach { a -> + when (a.status) { + AttendanceStatus.ATTEND -> a.user.removeAttend() + AttendanceStatus.ABSENT -> a.user.removeAbsent() + else -> Unit + } + } + attendanceRepository.deleteAllBySession(session) + sessionRepository.delete(session) + } + + fun find( + userId: Long, + sessionId: Long, + ): SessionResponse { + val user = userGetService.find(userId) + val session = sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException() } + return if (user.role == Role.ADMIN) { + sessionMapper.toAdminResponse(session) + } else { + sessionMapper.toResponse(session) + } + } + + fun findInfos(cardinal: Int?): SessionInfosResponse { + val sessions = + if (cardinal == null) { + sessionRepository.findAllByOrderByStartDesc() + } else { + sessionRepository.findAllByCardinalOrderByStartDesc(cardinal) + } + val thisWeek = findThisWeek(sessions) + val sorted = sessions.sortedByDescending { it.start } + return sessionMapper.toInfos(thisWeek, sorted) + } + + private fun findThisWeek( + sessions: List, + ): com.weeth.domain.attendance.domain.entity.Session? { + val today = LocalDate.now() + val startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) + val endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)) + return sessions.firstOrNull { s -> + val d = s.start.toLocalDate() + !d.isBefore(startOfWeek) && !d.isAfter(endOfWeek) + } + } +} diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt new file mode 100644 index 00000000..63cd6d6e --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt @@ -0,0 +1,50 @@ +package com.weeth.domain.schedule.application.usecase.command + +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest +import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest +import com.weeth.domain.schedule.application.dto.response.EventResponse +import com.weeth.domain.schedule.application.exception.EventNotFoundException +import com.weeth.domain.schedule.application.mapper.EventMapper +import com.weeth.domain.schedule.domain.repository.EventRepository +import com.weeth.domain.user.domain.service.CardinalGetService +import com.weeth.domain.user.domain.service.UserGetService +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class ManageEventUseCase( + private val eventRepository: EventRepository, + private val userGetService: UserGetService, + private val cardinalGetService: CardinalGetService, + private val eventMapper: EventMapper, +) { + fun find(eventId: Long): EventResponse = + eventMapper.toResponse(eventRepository.findById(eventId).orElseThrow { EventNotFoundException() }) + + @Transactional + fun create( + request: ScheduleSaveRequest, + userId: Long, + ) { + val user = userGetService.find(userId) + cardinalGetService.findByUserSide(request.cardinal) + eventRepository.save(eventMapper.toEntity(request, user)) + } + + @Transactional + fun update( + eventId: Long, + request: ScheduleUpdateRequest, + userId: Long, + ) { + val user = userGetService.find(userId) + val event = eventRepository.findById(eventId).orElseThrow { EventNotFoundException() } + event.update(request.title, request.content, request.location, request.start, request.end, user) + } + + @Transactional + fun delete(eventId: Long) { + val event = eventRepository.findById(eventId).orElseThrow { EventNotFoundException() } + eventRepository.delete(event) + } +} From 3842461532b454a4949aeff9f502df10bd115254 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 11:46:19 +0900 Subject: [PATCH 28/62] =?UTF-8?q?refactor:=20schedule=20usecase=20get=20qu?= =?UTF-8?q?ery=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/query/GetScheduleQueryService.kt | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt new file mode 100644 index 00000000..d7737910 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt @@ -0,0 +1,60 @@ +package com.weeth.domain.schedule.application.usecase.query + +import com.weeth.domain.attendance.domain.repository.SessionRepository +import com.weeth.domain.schedule.application.dto.response.ScheduleResponse +import com.weeth.domain.schedule.application.mapper.ScheduleMapper +import com.weeth.domain.schedule.domain.repository.EventRepository +import com.weeth.domain.user.domain.service.CardinalGetService +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.time.LocalDateTime +import java.util.AbstractMap +import java.util.stream.IntStream + +@Service +@Transactional(readOnly = true) +class GetScheduleQueryService( + private val eventRepository: EventRepository, + private val sessionRepository: SessionRepository, + private val cardinalGetService: CardinalGetService, + private val scheduleMapper: ScheduleMapper, +) { + fun findMonthly( + start: LocalDateTime, + end: LocalDateTime, + ): List { + val events = + eventRepository + .findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start) + .map { scheduleMapper.toResponse(it, false) } + val sessions = + sessionRepository + .findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start) + .map { scheduleMapper.toResponse(it, true) } + return (events + sessions).sortedBy { it.start } + } + + fun findYearly( + year: Int, + semester: Int, + ): Map> { + val cardinal = cardinalGetService.find(year, semester) + val events = + eventRepository + .findAllByCardinal(cardinal.cardinalNumber) + .map { scheduleMapper.toResponse(it, false) } + val sessions = + sessionRepository + .findAllByCardinal(cardinal.cardinalNumber) + .map { scheduleMapper.toResponse(it, true) } + + return (events + sessions) + .sortedBy { it.start } + .flatMap { schedule -> + IntStream + .range(schedule.start.monthValue, schedule.end.monthValue + 1) + .mapToObj { month -> AbstractMap.SimpleEntry(month, schedule) } + .toList() + }.groupBy({ it.key }, { it.value }) + } +} From 8ba2b6750631c01d9df80a80ca0f2d100515fe8c Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 11:48:20 +0900 Subject: [PATCH 29/62] =?UTF-8?q?refactor:=20usecase=20command/query=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9,=20=EB=B6=84=EB=A6=AC=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EC=B6=B0=20=EC=B0=B8=EC=A1=B0=20=ED=8C=8C=EC=9D=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/EventUseCase.java | 16 -- .../application/usecase/EventUseCaseImpl.java | 59 -------- .../application/usecase/MeetingUseCase.java | 19 --- .../usecase/MeetingUseCaseImpl.java | 140 ------------------ .../application/usecase/ScheduleUseCase.java | 15 -- .../usecase/ScheduleUseCaseImpl.java | 63 -------- .../domain/service/EventDeleteService.java | 17 --- .../domain/service/EventGetService.java | 37 ----- .../domain/service/EventSaveService.java | 17 --- .../domain/service/EventUpdateService.java | 18 --- .../domain/service/MeetingDeleteService.java | 17 --- .../domain/service/MeetingGetService.java | 54 ------- .../domain/service/MeetingSaveService.java | 17 --- .../domain/service/MeetingUpdateService.java | 14 -- .../presentation/EventAdminController.java | 18 +-- .../presentation/EventController.java | 6 +- .../presentation/MeetingAdminController.java | 6 +- .../presentation/MeetingController.java | 6 +- .../presentation/ScheduleController.java | 8 +- .../usecase/UserManageUseCaseImpl.java | 23 ++- .../command/CheckInAttendanceUseCase.kt | 38 ----- .../usecase/command/CloseAttendanceUseCase.kt | 53 ------- .../command/UpdateAttendanceStatusUseCase.kt | 34 ----- .../query/GetAttendanceQueryService.kt | 7 +- .../domain/service/AttendanceDeleteService.kt | 14 -- .../domain/service/AttendanceGetService.kt | 14 -- .../domain/service/AttendanceSaveService.kt | 29 ---- .../domain/service/AttendanceUpdateService.kt | 33 ----- .../infrastructure/AttendanceScheduler.kt | 6 +- .../presentation/AttendanceAdminController.kt | 20 +-- .../presentation/AttendanceController.kt | 6 +- 31 files changed, 55 insertions(+), 769 deletions(-) delete mode 100644 src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCase.java delete mode 100644 src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCaseImpl.java delete mode 100644 src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCase.java delete mode 100644 src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java delete mode 100644 src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCase.java delete mode 100644 src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImpl.java delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/service/EventDeleteService.java delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/service/EventGetService.java delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/service/EventSaveService.java delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/service/MeetingDeleteService.java delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/service/MeetingSaveService.java delete mode 100644 src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java delete mode 100644 src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/CheckInAttendanceUseCase.kt delete mode 100644 src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/CloseAttendanceUseCase.kt delete mode 100644 src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/UpdateAttendanceStatusUseCase.kt delete mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceDeleteService.kt delete mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceGetService.kt delete mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveService.kt delete mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceUpdateService.kt diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCase.java b/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCase.java deleted file mode 100644 index eafd896a..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCase.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.weeth.domain.schedule.application.usecase; - -import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest; -import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; -import com.weeth.domain.schedule.application.dto.response.EventResponse; - -public interface EventUseCase { - - EventResponse find(Long eventId); - - void save(ScheduleSaveRequest dto, Long userId); - - void update(Long eventId, ScheduleUpdateRequest dto, Long userId); - - void delete(Long eventId); -} diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCaseImpl.java b/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCaseImpl.java deleted file mode 100644 index d6e18883..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/EventUseCaseImpl.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.weeth.domain.schedule.application.usecase; - -import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest; -import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; -import com.weeth.domain.schedule.application.dto.response.EventResponse; -import com.weeth.domain.schedule.application.mapper.EventMapper; -import com.weeth.domain.schedule.domain.entity.Event; -import com.weeth.domain.schedule.domain.service.EventDeleteService; -import com.weeth.domain.schedule.domain.service.EventGetService; -import com.weeth.domain.schedule.domain.service.EventSaveService; -import com.weeth.domain.schedule.domain.service.EventUpdateService; -import com.weeth.domain.user.domain.entity.User; -import com.weeth.domain.user.domain.service.CardinalGetService; -import com.weeth.domain.user.domain.service.UserGetService; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -public class EventUseCaseImpl implements EventUseCase { - - private final UserGetService userGetService; - private final EventGetService eventGetService; - private final EventSaveService eventSaveService; - private final EventUpdateService eventUpdateService; - private final EventDeleteService eventDeleteService; - private final CardinalGetService cardinalGetService; - private final EventMapper mapper; - - @Override - public EventResponse find(Long eventId) { - return mapper.toResponse(eventGetService.find(eventId)); - } - - @Override - @Transactional - public void save(ScheduleSaveRequest dto, Long userId) { - User user = userGetService.find(userId); - cardinalGetService.findByUserSide(dto.getCardinal()); - - eventSaveService.save(mapper.toEntity(dto, user)); - } - - @Override - @Transactional - public void update(Long eventId, ScheduleUpdateRequest dto, Long userId) { - User user = userGetService.find(userId); - Event event = eventGetService.find(eventId); - eventUpdateService.update(event, dto, user); - } - - @Override - @Transactional - public void delete(Long eventId) { - Event event = eventGetService.find(eventId); - eventDeleteService.delete(event); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCase.java b/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCase.java deleted file mode 100644 index d0d76c94..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCase.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.weeth.domain.schedule.application.usecase; - -import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest; -import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; -import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse; -import com.weeth.domain.schedule.application.dto.response.SessionResponse; - -public interface MeetingUseCase { - - SessionResponse find(Long userId, Long eventId); - - SessionInfosResponse find(Integer cardinal); - - void save(ScheduleSaveRequest dto, Long userId); - - void update(ScheduleUpdateRequest dto, Long userId, Long meetingId); - - void delete(Long meetingId); -} diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java b/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java deleted file mode 100644 index e095258b..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImpl.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.weeth.domain.schedule.application.usecase; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import com.weeth.domain.attendance.domain.entity.Attendance; -import com.weeth.domain.attendance.domain.entity.Session; -import com.weeth.domain.attendance.domain.service.AttendanceDeleteService; -import com.weeth.domain.attendance.domain.service.AttendanceGetService; -import com.weeth.domain.attendance.domain.service.AttendanceSaveService; -import com.weeth.domain.attendance.domain.service.AttendanceUpdateService; -import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest; -import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; -import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse; -import com.weeth.domain.schedule.application.dto.response.SessionResponse; -import com.weeth.domain.schedule.application.mapper.SessionMapper; -import com.weeth.domain.schedule.domain.service.MeetingDeleteService; -import com.weeth.domain.schedule.domain.service.MeetingGetService; -import com.weeth.domain.schedule.domain.service.MeetingSaveService; -import com.weeth.domain.schedule.domain.service.MeetingUpdateService; -import com.weeth.domain.user.domain.entity.Cardinal; -import com.weeth.domain.user.domain.entity.User; -import com.weeth.domain.user.domain.entity.enums.Role; -import com.weeth.domain.user.domain.service.CardinalGetService; -import com.weeth.domain.user.domain.service.UserGetService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.DayOfWeek; -import java.time.LocalDate; -import java.time.temporal.TemporalAdjusters; -import java.util.Comparator; -import java.util.List; - -@Slf4j -@Service -@RequiredArgsConstructor -public class MeetingUseCaseImpl implements MeetingUseCase { - - private final MeetingGetService meetingGetService; - private final SessionMapper mapper; - private final MeetingSaveService meetingSaveService; - private final UserGetService userGetService; - private final MeetingUpdateService meetingUpdateService; - private final MeetingDeleteService meetingDeleteService; - private final AttendanceGetService attendanceGetService; - private final AttendanceSaveService attendanceSaveService; - private final AttendanceDeleteService attendanceDeleteService; - private final AttendanceUpdateService attendanceUpdateService; - private final CardinalGetService cardinalGetService; - - @PersistenceContext - private EntityManager em; - - @Override - public SessionResponse find(Long userId, Long sessionId) { - User user = userGetService.find(userId); - Session session = meetingGetService.find(sessionId); - - if (Role.ADMIN == user.getRole()) { - return mapper.toAdminResponse(session); - } - - return mapper.toResponse(session); - } - - @Override - public SessionInfosResponse find(Integer cardinal) { - List sessions; - - if (cardinal == null) { - sessions = meetingGetService.findAll(); - } else { - sessions = meetingGetService.findMeetingByCardinal(cardinal); - } - - Session thisWeek = findThisWeek(sessions); - List sorted = sortSessions(sessions); - - return mapper.toInfos(thisWeek, sorted); - } - - @Override - @Transactional - public void save(ScheduleSaveRequest dto, Long userId) { - User user = userGetService.find(userId); - Cardinal cardinal = cardinalGetService.findByUserSide(dto.getCardinal()); - - List userList = userGetService.findAllByCardinal(cardinal); - - Session session = mapper.toEntity(dto, user); - meetingSaveService.save(session); - - attendanceSaveService.saveAll(userList, session); - } - - @Override - @Transactional - public void update(ScheduleUpdateRequest dto, Long userId, Long sessionId) { - Session session = meetingGetService.find(sessionId); - User user = userGetService.find(userId); - meetingUpdateService.update(dto, user, session); - } - - @Override - @Transactional - public void delete(Long sessionId) { - Session session = meetingGetService.find(sessionId); - List attendances = attendanceGetService.findAllByMeeting(session); - - attendanceUpdateService.updateUserAttendanceByStatus(attendances); - - em.flush(); - em.clear(); - - attendanceDeleteService.deleteAll(session); - meetingDeleteService.delete(session); - } - - private List sortSessions(List sessions) { - return sessions.stream() - .sorted(Comparator.comparing(Session::getStart).reversed()) - .toList(); - } - - private Session findThisWeek(List sessions) { - LocalDate today = LocalDate.now(); - LocalDate startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); - LocalDate endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); - - return sessions.stream() - .filter(s -> { - LocalDate d = s.getStart().toLocalDate(); - return !d.isBefore(startOfWeek) && !d.isAfter(endOfWeek); - }) - .findFirst() - .orElse(null); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCase.java b/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCase.java deleted file mode 100644 index 9721cb6a..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCase.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.weeth.domain.schedule.application.usecase; - -import com.weeth.domain.schedule.application.dto.response.ScheduleResponse; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -public interface ScheduleUseCase { - - List findByMonthly(LocalDateTime start, LocalDateTime end); - - Map> findByYearly(Integer year, Integer semester); - -} diff --git a/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImpl.java b/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImpl.java deleted file mode 100644 index 958574a7..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImpl.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.weeth.domain.schedule.application.usecase; - -import com.weeth.domain.schedule.application.dto.response.ScheduleResponse; -import com.weeth.domain.schedule.domain.service.EventGetService; -import com.weeth.domain.schedule.domain.service.MeetingGetService; -import com.weeth.domain.user.domain.entity.Cardinal; -import com.weeth.domain.user.domain.service.CardinalGetService; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.time.LocalDateTime; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -@Service -@RequiredArgsConstructor -public class ScheduleUseCaseImpl implements ScheduleUseCase { - - private final EventGetService eventGetService; - private final MeetingGetService meetingGetService; - private final CardinalGetService cardinalGetService; - - @Override - public List findByMonthly(LocalDateTime start, LocalDateTime end) { - List events = eventGetService.find(start, end); - List meetings = meetingGetService.find(start, end); - - return Stream.of(events, meetings) - .flatMap(Collection::stream) - .sorted(Comparator.comparing(ScheduleResponse::getStart)) - .toList(); - } - - @Override - public Map> findByYearly(Integer year, Integer semester) { - Cardinal cardinal = cardinalGetService.find(year, semester); - - List events = eventGetService.find(cardinal.getCardinalNumber()); - List meetings = meetingGetService.findByCardinal(cardinal.getCardinalNumber()); - - return Stream.of(events, meetings) - .flatMap(Collection::stream) - .sorted(Comparator.comparing(ScheduleResponse::getStart)) - .flatMap(schedule -> { - List> monthEventPairs = new ArrayList<>(); - - int left = schedule.getStart().getMonthValue(); - int right = schedule.getEnd().getMonthValue() + 1; - IntStream.range(left, right) - .forEach(month -> monthEventPairs.add( - new AbstractMap.SimpleEntry<>(month, schedule)) - ); - - return monthEventPairs.stream(); - }) - .collect(Collectors.groupingBy( - Map.Entry::getKey, - Collectors.mapping(Map.Entry::getValue, Collectors.toList()) - )); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/EventDeleteService.java b/src/main/java/com/weeth/domain/schedule/domain/service/EventDeleteService.java deleted file mode 100644 index 1bcef851..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/service/EventDeleteService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.weeth.domain.schedule.domain.service; - -import com.weeth.domain.schedule.domain.entity.Event; -import com.weeth.domain.schedule.domain.repository.EventRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class EventDeleteService { - - private final EventRepository eventRepository; - - public void delete(Event event) { - eventRepository.delete(event); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/EventGetService.java b/src/main/java/com/weeth/domain/schedule/domain/service/EventGetService.java deleted file mode 100644 index 3560e4ff..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/service/EventGetService.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.weeth.domain.schedule.domain.service; - -import com.weeth.domain.schedule.application.dto.response.ScheduleResponse; -import com.weeth.domain.schedule.application.mapper.ScheduleMapper; -import com.weeth.domain.schedule.domain.entity.Event; -import com.weeth.domain.schedule.domain.repository.EventRepository; -import com.weeth.domain.schedule.application.exception.EventNotFoundException; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.time.LocalDateTime; -import java.util.List; - -@Service -@RequiredArgsConstructor -public class EventGetService { - - private final EventRepository eventRepository; - private final ScheduleMapper mapper; - - public Event find(Long eventId) { - return eventRepository.findById(eventId) - .orElseThrow(EventNotFoundException::new); - } - - public List find(LocalDateTime start, LocalDateTime end) { - return eventRepository.findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start).stream() - .map(event -> mapper.toResponse(event, false)) - .toList(); - } - - public List find(Integer cardinal) { - return eventRepository.findAllByCardinal(cardinal).stream() - .map(event -> mapper.toResponse(event, false)) - .toList(); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/EventSaveService.java b/src/main/java/com/weeth/domain/schedule/domain/service/EventSaveService.java deleted file mode 100644 index b2c82831..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/service/EventSaveService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.weeth.domain.schedule.domain.service; - -import com.weeth.domain.schedule.domain.entity.Event; -import com.weeth.domain.schedule.domain.repository.EventRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class EventSaveService { - - private final EventRepository eventRepository; - - public void save(Event event) { - eventRepository.save(event); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java b/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java deleted file mode 100644 index 842fb2f9..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/service/EventUpdateService.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.weeth.domain.schedule.domain.service; - -import jakarta.transaction.Transactional; -import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; -import com.weeth.domain.schedule.domain.entity.Event; -import com.weeth.domain.user.domain.entity.User; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@Transactional -@RequiredArgsConstructor -public class EventUpdateService { - - public void update(Event event, ScheduleUpdateRequest dto, User user) { - event.update(dto.getTitle(), dto.getContent(), dto.getLocation(), dto.getStart(), dto.getEnd(), user); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingDeleteService.java b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingDeleteService.java deleted file mode 100644 index 502d3120..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingDeleteService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.weeth.domain.schedule.domain.service; - -import com.weeth.domain.attendance.domain.entity.Session; -import com.weeth.domain.attendance.domain.repository.SessionRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class MeetingDeleteService { - - private final SessionRepository sessionRepository; - - public void delete(Session session) { - sessionRepository.delete(session); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java deleted file mode 100644 index b0c1ea73..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingGetService.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.weeth.domain.schedule.domain.service; - -import com.weeth.domain.attendance.domain.entity.Session; -import com.weeth.domain.attendance.domain.entity.enums.SessionStatus; -import com.weeth.domain.attendance.domain.repository.SessionRepository; -import com.weeth.domain.schedule.application.dto.response.ScheduleResponse; -import com.weeth.domain.schedule.application.mapper.ScheduleMapper; -import com.weeth.domain.schedule.application.exception.MeetingNotFoundException; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.time.LocalDateTime; -import java.util.List; - -@Service -@RequiredArgsConstructor -public class MeetingGetService { - - private final SessionRepository sessionRepository; - private final ScheduleMapper mapper; - - public Session find(Long sessionId) { - return sessionRepository.findById(sessionId) - .orElseThrow(MeetingNotFoundException::new); - } - - public List find(LocalDateTime start, LocalDateTime end) { - return sessionRepository.findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start).stream() - .map(session -> mapper.toResponse(session, true)) - .toList(); - } - - public List find(Integer cardinal) { - return sessionRepository.findAllByCardinalOrderByStartAsc(cardinal); - } - - public List findMeetingByCardinal(Integer cardinal) { - return sessionRepository.findAllByCardinalOrderByStartDesc(cardinal); - } - - public List findAll() { - return sessionRepository.findAllByOrderByStartDesc(); - } - - public List findByCardinal(Integer cardinal) { - return sessionRepository.findAllByCardinal(cardinal).stream() - .map(session -> mapper.toResponse(session, true)) - .toList(); - } - - public List findAllOpenMeetingsBeforeNow() { - return sessionRepository.findAllByStatusAndEndBeforeOrderByEndAsc(SessionStatus.OPEN, LocalDateTime.now()); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingSaveService.java b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingSaveService.java deleted file mode 100644 index d31984b0..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingSaveService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.weeth.domain.schedule.domain.service; - -import com.weeth.domain.attendance.domain.entity.Session; -import com.weeth.domain.attendance.domain.repository.SessionRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class MeetingSaveService { - - private final SessionRepository sessionRepository; - - public void save(Session session) { - sessionRepository.save(session); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java b/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java deleted file mode 100644 index ee698f35..00000000 --- a/src/main/java/com/weeth/domain/schedule/domain/service/MeetingUpdateService.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.weeth.domain.schedule.domain.service; - -import com.weeth.domain.attendance.domain.entity.Session; -import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; -import com.weeth.domain.user.domain.entity.User; -import org.springframework.stereotype.Service; - -@Service -public class MeetingUpdateService { - - public void update(ScheduleUpdateRequest dto, User user, Session session) { - session.updateInfo(dto.getTitle(), dto.getContent(), dto.getLocation(), dto.getStart(), dto.getEnd(), user); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java b/src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java index eb11811f..88197b03 100644 --- a/src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java +++ b/src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java @@ -7,8 +7,8 @@ import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest; import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; import com.weeth.domain.schedule.application.exception.EventErrorCode; -import com.weeth.domain.schedule.application.usecase.EventUseCase; -import com.weeth.domain.schedule.application.usecase.MeetingUseCase; +import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase; +import com.weeth.domain.schedule.application.usecase.command.ManageEventUseCase; import com.weeth.domain.schedule.domain.entity.enums.Type; import com.weeth.global.auth.annotation.CurrentUser; import com.weeth.global.common.exception.ApiErrorCodeExample; @@ -25,17 +25,17 @@ @ApiErrorCodeExample(EventErrorCode.class) public class EventAdminController { - private final EventUseCase eventUseCase; - private final MeetingUseCase meetingUseCase; + private final ManageEventUseCase manageEventUseCase; + private final ManageSessionUseCase manageSessionUseCase; @PostMapping @Operation(summary = "일정/정기모임 생성") public CommonResponse save(@Valid @RequestBody ScheduleSaveRequest dto, @Parameter(hidden = true) @CurrentUser Long userId) { if (dto.getType() == Type.EVENT) { - eventUseCase.save(dto, userId); + manageEventUseCase.create(dto, userId); } else { - meetingUseCase.save(dto, userId); + manageSessionUseCase.create(dto, userId); } return CommonResponse.success(EVENT_SAVE_SUCCESS); @@ -46,9 +46,9 @@ public CommonResponse save(@Valid @RequestBody ScheduleSaveRequest dto, public CommonResponse update(@PathVariable Long eventId, @Valid @RequestBody ScheduleUpdateRequest dto, @Parameter(hidden = true) @CurrentUser Long userId) { if (dto.getType() == Type.EVENT) { - eventUseCase.update(eventId, dto, userId); + manageEventUseCase.update(eventId, dto, userId); } else { - meetingUseCase.update(dto, userId, eventId); + manageSessionUseCase.update(eventId, dto, userId); } return CommonResponse.success(EVENT_UPDATE_SUCCESS); @@ -57,7 +57,7 @@ public CommonResponse update(@PathVariable Long eventId, @Valid @RequestBo @DeleteMapping("/{eventId}") @Operation(summary = "일정 삭제") public CommonResponse delete(@PathVariable Long eventId) { - eventUseCase.delete(eventId); + manageEventUseCase.delete(eventId); return CommonResponse.success(EVENT_DELETE_SUCCESS); } diff --git a/src/main/java/com/weeth/domain/schedule/presentation/EventController.java b/src/main/java/com/weeth/domain/schedule/presentation/EventController.java index 2413105c..d73f0c78 100644 --- a/src/main/java/com/weeth/domain/schedule/presentation/EventController.java +++ b/src/main/java/com/weeth/domain/schedule/presentation/EventController.java @@ -4,7 +4,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import com.weeth.domain.schedule.application.dto.response.EventResponse; import com.weeth.domain.schedule.application.exception.EventErrorCode; -import com.weeth.domain.schedule.application.usecase.EventUseCase; +import com.weeth.domain.schedule.application.usecase.command.ManageEventUseCase; import com.weeth.global.common.exception.ApiErrorCodeExample; import com.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; @@ -22,13 +22,13 @@ @ApiErrorCodeExample(EventErrorCode.class) public class EventController { - private final EventUseCase eventUseCase; + private final ManageEventUseCase manageEventUseCase; @GetMapping("/{eventId}") @Operation(summary="일정 상세 조회") public CommonResponse find(@PathVariable Long eventId) { return CommonResponse.success(EVENT_FIND_SUCCESS, - eventUseCase.find(eventId)); + manageEventUseCase.find(eventId)); } } diff --git a/src/main/java/com/weeth/domain/schedule/presentation/MeetingAdminController.java b/src/main/java/com/weeth/domain/schedule/presentation/MeetingAdminController.java index dcbc3e19..15f32dbc 100644 --- a/src/main/java/com/weeth/domain/schedule/presentation/MeetingAdminController.java +++ b/src/main/java/com/weeth/domain/schedule/presentation/MeetingAdminController.java @@ -3,7 +3,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import com.weeth.domain.schedule.application.exception.MeetingErrorCode; -import com.weeth.domain.schedule.application.usecase.MeetingUseCase; +import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase; import com.weeth.global.common.exception.ApiErrorCodeExample; import com.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; @@ -21,12 +21,12 @@ @ApiErrorCodeExample(MeetingErrorCode.class) public class MeetingAdminController { - private final MeetingUseCase meetingUseCase; + private final ManageSessionUseCase manageSessionUseCase; @DeleteMapping("/{meetingId}") @Operation(summary = "정기모임 삭제") public CommonResponse delete(@PathVariable Long meetingId) { - meetingUseCase.delete(meetingId); + manageSessionUseCase.delete(meetingId); return CommonResponse.success(MEETING_DELETE_SUCCESS); } } diff --git a/src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java b/src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java index a4a89363..a1d680aa 100644 --- a/src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java +++ b/src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java @@ -5,7 +5,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import com.weeth.domain.schedule.application.dto.response.SessionResponse; import com.weeth.domain.schedule.application.exception.MeetingErrorCode; -import com.weeth.domain.schedule.application.usecase.MeetingUseCase; +import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase; import com.weeth.global.auth.annotation.CurrentUser; import com.weeth.global.common.exception.ApiErrorCodeExample; import com.weeth.global.common.response.CommonResponse; @@ -24,12 +24,12 @@ @ApiErrorCodeExample(MeetingErrorCode.class) public class MeetingController { - private final MeetingUseCase meetingUseCase; + private final ManageSessionUseCase manageSessionUseCase; @GetMapping("/{meetingId}") @Operation(summary="정기모임 상세 조회") public CommonResponse find(@Parameter(hidden = true) @CurrentUser Long userId, @PathVariable Long meetingId) { - return CommonResponse.success(MEETING_FIND_SUCCESS, meetingUseCase.find(userId, meetingId)); + return CommonResponse.success(MEETING_FIND_SUCCESS, manageSessionUseCase.find(userId, meetingId)); } } diff --git a/src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java b/src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java index 6a6a758f..4327031d 100644 --- a/src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java +++ b/src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java @@ -5,7 +5,7 @@ import com.weeth.domain.schedule.application.dto.response.ScheduleResponse; import com.weeth.domain.schedule.application.exception.EventErrorCode; import com.weeth.domain.schedule.application.exception.MeetingErrorCode; -import com.weeth.domain.schedule.application.usecase.ScheduleUseCase; +import com.weeth.domain.schedule.application.usecase.query.GetScheduleQueryService; import com.weeth.global.common.exception.ApiErrorCodeExample; import com.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; @@ -29,14 +29,14 @@ @ApiErrorCodeExample({EventErrorCode.class, MeetingErrorCode.class}) public class ScheduleController { - private final ScheduleUseCase scheduleUseCase; + private final GetScheduleQueryService getScheduleQueryService; @GetMapping("/monthly") @Operation(summary="월별 일정 조회") public CommonResponse> findByMonthly( @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start, @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime end) { - return CommonResponse.success(SCHEDULE_MONTHLY_FIND_SUCCESS, scheduleUseCase.findByMonthly(start, end)); + return CommonResponse.success(SCHEDULE_MONTHLY_FIND_SUCCESS, getScheduleQueryService.findMonthly(start, end)); } @GetMapping("/yearly") @@ -44,6 +44,6 @@ public CommonResponse> findByMonthly( public CommonResponse>> findByYearly( @RequestParam Integer year, @RequestParam Integer semester) { - return CommonResponse.success(SCHEDULE_YEARLY_FIND_SUCCESS, scheduleUseCase.findByYearly(year, semester)); + return CommonResponse.success(SCHEDULE_YEARLY_FIND_SUCCESS, getScheduleQueryService.findYearly(year, semester)); } } diff --git a/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java b/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java index e2b314c0..1abdc9ba 100644 --- a/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java +++ b/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java @@ -1,9 +1,10 @@ package com.weeth.domain.user.application.usecase; import jakarta.transaction.Transactional; -import com.weeth.domain.attendance.domain.service.AttendanceSaveService; +import com.weeth.domain.attendance.domain.entity.Attendance; import com.weeth.domain.attendance.domain.entity.Session; -import com.weeth.domain.schedule.domain.service.MeetingGetService; +import com.weeth.domain.attendance.domain.repository.AttendanceRepository; +import com.weeth.domain.attendance.domain.repository.SessionRepository; import com.weeth.domain.user.application.exception.InvalidUserOrderException; import com.weeth.domain.user.application.mapper.UserMapper; import com.weeth.domain.user.domain.entity.Cardinal; @@ -33,8 +34,8 @@ public class UserManageUseCaseImpl implements UserManageUseCase { private final UserUpdateService userUpdateService; private final UserDeleteService userDeleteService; - private final AttendanceSaveService attendanceSaveService; - private final MeetingGetService meetingGetService; + private final AttendanceRepository attendanceRepository; + private final SessionRepository sessionRepository; private final JwtRedisService jwtRedisService; private final CardinalGetService cardinalGetService; private final UserCardinalSaveService userCardinalSaveService; @@ -95,8 +96,11 @@ public void accept(UserId userIds) { if (user.isInactive()) { userUpdateService.accept(user); - List sessions = meetingGetService.find(cardinal); - attendanceSaveService.init(user, sessions); + List sessions = sessionRepository.findAllByCardinalOrderByStartAsc(cardinal); + List attendances = sessions.stream() + .map(s -> Attendance.Companion.create(s, user)) + .collect(java.util.stream.Collectors.toList()); + attendanceRepository.saveAll(attendances); } }); } @@ -140,8 +144,11 @@ public void applyOB(List requests) { if (userCardinalGetService.notContains(user, nextCardinal)) { if (userCardinalGetService.isCurrent(user, nextCardinal)) { user.initAttendance(); - List sessionList = meetingGetService.find(request.cardinal()); - attendanceSaveService.init(user, sessionList); + List sessionList = sessionRepository.findAllByCardinalOrderByStartAsc(request.cardinal()); + List attendances = sessionList.stream() + .map(s -> Attendance.Companion.create(s, user)) + .collect(java.util.stream.Collectors.toList()); + attendanceRepository.saveAll(attendances); } UserCardinal userCardinal = new UserCardinal(user, nextCardinal); diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/CheckInAttendanceUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/CheckInAttendanceUseCase.kt deleted file mode 100644 index 5547b43f..00000000 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/CheckInAttendanceUseCase.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.weeth.domain.attendance.application.usecase.command - -import com.weeth.domain.attendance.application.exception.AttendanceCodeMismatchException -import com.weeth.domain.attendance.application.exception.AttendanceNotFoundException -import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus -import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.user.domain.service.UserGetService -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import java.time.LocalDateTime - -@Service -class CheckInAttendanceUseCase( - private val userGetService: UserGetService, - private val attendanceRepository: AttendanceRepository, -) { - @Transactional - fun checkIn( - userId: Long, - code: Int, - ) { - val user = userGetService.find(userId) - val now = LocalDateTime.now() - - val todayAttendance = - attendanceRepository.findCurrentByUserId(userId, now, now.plusMinutes(10)) - ?: throw AttendanceNotFoundException() - - if (todayAttendance.isWrong(code)) { - throw AttendanceCodeMismatchException() - } - - if (todayAttendance.status != AttendanceStatus.ATTEND) { - todayAttendance.attend() - user.attend() - } - } -} diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/CloseAttendanceUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/CloseAttendanceUseCase.kt deleted file mode 100644 index 0f150311..00000000 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/CloseAttendanceUseCase.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.weeth.domain.attendance.application.usecase.command - -import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.schedule.application.exception.MeetingNotFoundException -import com.weeth.domain.schedule.domain.service.MeetingGetService -import com.weeth.domain.user.domain.entity.enums.Status -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import java.time.LocalDate - -@Service -class CloseAttendanceUseCase( - private val meetingGetService: MeetingGetService, - private val attendanceRepository: AttendanceRepository, -) { - @Transactional - fun close( - now: LocalDate, - cardinal: Int, - ) { - val sessions = meetingGetService.find(cardinal) - - val targetSession = - sessions.firstOrNull { session -> - session.start.toLocalDate().isEqual(now) && - session.end.toLocalDate().isEqual(now) - } ?: throw MeetingNotFoundException() - - val attendanceList = attendanceRepository.findAllBySessionAndUserStatus(targetSession, Status.ACTIVE) - closePendingAttendances(attendanceList) - } - - @Transactional - fun autoClose() { - val sessions = meetingGetService.findAllOpenMeetingsBeforeNow() - - sessions.forEach { session -> - session.close() - val attendanceList = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) - closePendingAttendances(attendanceList) - } - } - - private fun closePendingAttendances(attendances: List) { - attendances - .filter { it.isPending() } - .forEach { attendance -> - attendance.close() - attendance.user.absent() - } - } -} diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/UpdateAttendanceStatusUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/UpdateAttendanceStatusUseCase.kt deleted file mode 100644 index 2752972b..00000000 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/UpdateAttendanceStatusUseCase.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.weeth.domain.attendance.application.usecase.command - -import com.weeth.domain.attendance.application.dto.request.UpdateAttendanceStatusRequest -import com.weeth.domain.attendance.application.exception.AttendanceNotFoundException -import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus -import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional - -@Service -class UpdateAttendanceStatusUseCase( - private val attendanceRepository: AttendanceRepository, -) { - @Transactional - fun updateStatus(attendanceUpdates: List) { - attendanceUpdates.forEach { update -> - val attendance = - attendanceRepository.findByIdWithUser(update.attendanceId) - ?: throw AttendanceNotFoundException() - val user = attendance.user - val newStatus = AttendanceStatus.valueOf(update.status) - - if (newStatus == AttendanceStatus.ABSENT) { - attendance.close() - user.removeAttend() - user.absent() - } else { - attendance.attend() - user.removeAbsent() - user.attend() - } - } - } -} diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt index 42144664..1120247b 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt @@ -5,7 +5,8 @@ import com.weeth.domain.attendance.application.dto.response.AttendanceInfoRespon import com.weeth.domain.attendance.application.dto.response.AttendanceSummaryResponse import com.weeth.domain.attendance.application.mapper.AttendanceMapper import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.schedule.domain.service.MeetingGetService +import com.weeth.domain.attendance.domain.repository.SessionRepository +import com.weeth.domain.schedule.application.exception.MeetingNotFoundException import com.weeth.domain.user.domain.entity.enums.Role import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.service.UserCardinalGetService @@ -19,7 +20,7 @@ import java.time.LocalDate class GetAttendanceQueryService( private val userGetService: UserGetService, private val userCardinalGetService: UserCardinalGetService, - private val meetingGetService: MeetingGetService, + private val sessionRepository: SessionRepository, private val attendanceRepository: AttendanceRepository, private val mapper: AttendanceMapper, ) { @@ -50,7 +51,7 @@ class GetAttendanceQueryService( } fun findAllAttendanceByMeeting(sessionId: Long): List { - val session = meetingGetService.find(sessionId) + val session = sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException() } val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) return attendances.map(mapper::toInfoResponse) } diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceDeleteService.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceDeleteService.kt deleted file mode 100644 index b6549f45..00000000 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceDeleteService.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.weeth.domain.attendance.domain.service - -import com.weeth.domain.attendance.domain.entity.Session -import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import org.springframework.stereotype.Service - -@Service -class AttendanceDeleteService( - private val attendanceRepository: AttendanceRepository, -) { - fun deleteAll(session: Session) { - attendanceRepository.deleteAllBySession(session) - } -} diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceGetService.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceGetService.kt deleted file mode 100644 index 8fff7606..00000000 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceGetService.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.weeth.domain.attendance.domain.service - -import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.Session -import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.user.domain.entity.enums.Status -import org.springframework.stereotype.Service - -@Service -class AttendanceGetService( - private val attendanceRepository: AttendanceRepository, -) { - fun findAllByMeeting(session: Session): List = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) -} diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveService.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveService.kt deleted file mode 100644 index ca870e7c..00000000 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveService.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.weeth.domain.attendance.domain.service - -import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.Session -import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.user.domain.entity.User -import org.springframework.stereotype.Service - -@Service -class AttendanceSaveService( - private val attendanceRepository: AttendanceRepository, -) { - fun init( - user: User, - sessions: List?, - ) { - sessions?.forEach { session -> - attendanceRepository.save(Attendance.create(session, user)) - } - } - - fun saveAll( - userList: List, - session: Session, - ) { - val attendances = userList.map { user -> Attendance.create(session, user) } - attendanceRepository.saveAll(attendances) - } -} diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceUpdateService.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceUpdateService.kt deleted file mode 100644 index 63267299..00000000 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/service/AttendanceUpdateService.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.weeth.domain.attendance.domain.service - -import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus -import org.springframework.stereotype.Service - -@Service -class AttendanceUpdateService { - fun attend(attendance: Attendance) { - attendance.attend() - attendance.user.attend() - } - - fun close(attendances: List) { - attendances - .filter { it.isPending() } - .forEach { attendance -> - attendance.close() - attendance.user.absent() - } - } - - fun updateUserAttendanceByStatus(attendances: List) { - attendances.forEach { attendance -> - val user = attendance.user - if (attendance.status == AttendanceStatus.ATTEND) { - user.removeAttend() - } else { - user.removeAbsent() - } - } - } -} diff --git a/src/main/kotlin/com/weeth/domain/attendance/infrastructure/AttendanceScheduler.kt b/src/main/kotlin/com/weeth/domain/attendance/infrastructure/AttendanceScheduler.kt index a41113e6..9890be6a 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/infrastructure/AttendanceScheduler.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/infrastructure/AttendanceScheduler.kt @@ -1,15 +1,15 @@ package com.weeth.domain.attendance.infrastructure -import com.weeth.domain.attendance.application.usecase.command.CloseAttendanceUseCase +import com.weeth.domain.attendance.application.usecase.command.ManageAttendanceUseCase import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Component @Component class AttendanceScheduler( - private val closeAttendanceUseCase: CloseAttendanceUseCase, + private val manageAttendanceUseCase: ManageAttendanceUseCase, ) { @Scheduled(cron = "0 0 22 * * THU", zone = "Asia/Seoul") fun autoCloseAttendance() { - closeAttendanceUseCase.autoClose() + manageAttendanceUseCase.autoClose() } } diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt index 2925ea2e..b8cf9204 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt @@ -3,11 +3,10 @@ package com.weeth.domain.attendance.presentation import com.weeth.domain.attendance.application.dto.request.UpdateAttendanceStatusRequest import com.weeth.domain.attendance.application.dto.response.AttendanceInfoResponse import com.weeth.domain.attendance.application.exception.AttendanceErrorCode -import com.weeth.domain.attendance.application.usecase.command.CloseAttendanceUseCase -import com.weeth.domain.attendance.application.usecase.command.UpdateAttendanceStatusUseCase +import com.weeth.domain.attendance.application.usecase.command.ManageAttendanceUseCase +import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase import com.weeth.domain.attendance.application.usecase.query.GetAttendanceQueryService import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse -import com.weeth.domain.schedule.application.usecase.MeetingUseCase import com.weeth.global.common.exception.ApiErrorCodeExample import com.weeth.global.common.response.CommonResponse import io.swagger.v3.oas.annotations.Operation @@ -27,10 +26,9 @@ import java.time.LocalDate @RequestMapping("/api/v1/admin/attendances") @ApiErrorCodeExample(AttendanceErrorCode::class) class AttendanceAdminController( - private val closeAttendanceUseCase: CloseAttendanceUseCase, - private val updateAttendanceStatusUseCase: UpdateAttendanceStatusUseCase, + private val manageAttendanceUseCase: ManageAttendanceUseCase, + private val manageSessionUseCase: ManageSessionUseCase, private val getAttendanceQueryService: GetAttendanceQueryService, - private val meetingUseCase: MeetingUseCase, ) { @PatchMapping @Operation(summary = "출석 마감") @@ -38,7 +36,7 @@ class AttendanceAdminController( @RequestParam now: LocalDate, @RequestParam cardinal: Int, ): CommonResponse { - closeAttendanceUseCase.close(now, cardinal) + manageAttendanceUseCase.close(now, cardinal) return CommonResponse.success(AttendanceResponseCode.ATTENDANCE_CLOSE_SUCCESS) } @@ -46,10 +44,8 @@ class AttendanceAdminController( @Operation(summary = "정기모임 조회") fun getMeetings( @RequestParam(required = false) cardinal: Int?, - ): CommonResponse { - val response = meetingUseCase.find(cardinal) - return CommonResponse.success(AttendanceResponseCode.MEETING_FIND_SUCCESS, response) - } + ): CommonResponse = + CommonResponse.success(AttendanceResponseCode.MEETING_FIND_SUCCESS, manageSessionUseCase.findInfos(cardinal)) @GetMapping("/{meetingId}") @Operation(summary = "모든 인원 정기모임 출석 정보 조회") @@ -66,7 +62,7 @@ class AttendanceAdminController( fun updateAttendanceStatus( @RequestBody @Valid attendanceUpdates: List, ): CommonResponse { - updateAttendanceStatusUseCase.updateStatus(attendanceUpdates) + manageAttendanceUseCase.updateStatus(attendanceUpdates) return CommonResponse.success(AttendanceResponseCode.ATTENDANCE_UPDATED_SUCCESS) } } diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt index 8a1256b7..fd6c02b8 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt @@ -4,7 +4,7 @@ import com.weeth.domain.attendance.application.dto.request.CheckInRequest import com.weeth.domain.attendance.application.dto.response.AttendanceDetailResponse import com.weeth.domain.attendance.application.dto.response.AttendanceSummaryResponse import com.weeth.domain.attendance.application.exception.AttendanceErrorCode -import com.weeth.domain.attendance.application.usecase.command.CheckInAttendanceUseCase +import com.weeth.domain.attendance.application.usecase.command.ManageAttendanceUseCase import com.weeth.domain.attendance.application.usecase.query.GetAttendanceQueryService import com.weeth.global.auth.annotation.CurrentUser import com.weeth.global.common.exception.ApiErrorCodeExample @@ -23,7 +23,7 @@ import org.springframework.web.bind.annotation.RestController @RequestMapping("/api/v1/attendances") @ApiErrorCodeExample(AttendanceErrorCode::class) class AttendanceController( - private val checkInAttendanceUseCase: CheckInAttendanceUseCase, + private val manageAttendanceUseCase: ManageAttendanceUseCase, private val getAttendanceQueryService: GetAttendanceQueryService, ) { @PatchMapping @@ -32,7 +32,7 @@ class AttendanceController( @Parameter(hidden = true) @CurrentUser userId: Long, @RequestBody checkIn: CheckInRequest, ): CommonResponse { - checkInAttendanceUseCase.checkIn(userId, checkIn.code) + manageAttendanceUseCase.checkIn(userId, checkIn.code) return CommonResponse.success(AttendanceResponseCode.ATTENDANCE_CHECKIN_SUCCESS) } From 738b63ec1ec9b0c621e1cbfc4ad9e6567e64d6f2 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 11:48:59 +0900 Subject: [PATCH 30/62] =?UTF-8?q?refactor:=20usecase=20command/query=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9,=20=EB=B6=84=EB=A6=AC=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EC=B6=B0=20test=20=EC=88=98=EC=A0=95,=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/CheckInAttendanceUseCaseTest.kt | 88 -------- .../command/CloseAttendanceUseCaseTest.kt | 62 ------ .../UpdateAttendanceStatusUseCaseTest.kt | 68 ------ .../query/GetAttendanceQueryServiceTest.kt | 9 +- .../service/AttendanceSaveServiceTest.kt | 53 ----- .../usecase/MeetingUseCaseImplTest.kt | 209 ------------------ .../usecase/ScheduleUseCaseImplTest.kt | 52 ----- .../usecase/UserManageUseCaseTest.kt | 25 ++- 8 files changed, 18 insertions(+), 548 deletions(-) delete mode 100644 src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/CheckInAttendanceUseCaseTest.kt delete mode 100644 src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/CloseAttendanceUseCaseTest.kt delete mode 100644 src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/UpdateAttendanceStatusUseCaseTest.kt delete mode 100644 src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt delete mode 100644 src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt delete mode 100644 src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/CheckInAttendanceUseCaseTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/CheckInAttendanceUseCaseTest.kt deleted file mode 100644 index ab56d3bd..00000000 --- a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/CheckInAttendanceUseCaseTest.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.weeth.domain.attendance.application.usecase.command - -import com.weeth.domain.attendance.application.exception.AttendanceCodeMismatchException -import com.weeth.domain.attendance.application.exception.AttendanceNotFoundException -import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus -import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.user.domain.entity.User -import com.weeth.domain.user.domain.service.UserGetService -import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.DescribeSpec -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify - -class CheckInAttendanceUseCaseTest : - DescribeSpec({ - - val userId = 10L - val userGetService = mockk() - val attendanceRepository = mockk() - - val useCase = CheckInAttendanceUseCase(userGetService, attendanceRepository) - - describe("checkIn") { - context("진행 중 정기모임이고 코드 일치하며 상태가 ATTEND가 아닐 때") { - it("출석 처리된다") { - val user = mockk() - val attendance = mockk(relaxUnitFun = true) - every { attendance.isWrong(1234) } returns false - every { attendance.status } returns AttendanceStatus.PENDING - - every { userGetService.find(userId) } returns user - every { attendanceRepository.findCurrentByUserId(eq(userId), any(), any()) } returns attendance - every { user.attend() } returns Unit - - useCase.checkIn(userId, 1234) - - verify { attendance.attend() } - verify { user.attend() } - } - } - - context("진행 중 정기모임이 없을 때") { - it("AttendanceNotFoundException") { - val user = mockk() - every { userGetService.find(userId) } returns user - every { attendanceRepository.findCurrentByUserId(eq(userId), any(), any()) } returns null - - shouldThrow { - useCase.checkIn(userId, 1234) - } - } - } - - context("코드 불일치 시") { - it("AttendanceCodeMismatchException") { - val user = mockk() - val attendance = mockk() - every { attendance.isWrong(9999) } returns true - - every { userGetService.find(userId) } returns user - every { attendanceRepository.findCurrentByUserId(eq(userId), any(), any()) } returns attendance - - shouldThrow { - useCase.checkIn(userId, 9999) - } - } - } - - context("이미 ATTEND일 때") { - it("추가 처리 없이 종료") { - val user = mockk() - val attendance = mockk() - every { attendance.isWrong(1234) } returns false - every { attendance.status } returns AttendanceStatus.ATTEND - - every { userGetService.find(userId) } returns user - every { attendanceRepository.findCurrentByUserId(eq(userId), any(), any()) } returns attendance - - useCase.checkIn(userId, 1234) - - verify(exactly = 0) { attendance.attend() } - verify(exactly = 0) { user.attend() } - } - } - } - }) diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/CloseAttendanceUseCaseTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/CloseAttendanceUseCaseTest.kt deleted file mode 100644 index 8ac7df39..00000000 --- a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/CloseAttendanceUseCaseTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.weeth.domain.attendance.application.usecase.command - -import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createOneDaySession -import com.weeth.domain.schedule.application.exception.MeetingNotFoundException -import com.weeth.domain.schedule.domain.service.MeetingGetService -import com.weeth.domain.user.domain.entity.User -import com.weeth.domain.user.domain.entity.enums.Status -import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.DescribeSpec -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import java.time.LocalDate - -class CloseAttendanceUseCaseTest : - DescribeSpec({ - - val meetingGetService = mockk() - val attendanceRepository = mockk() - - val useCase = CloseAttendanceUseCase(meetingGetService, attendanceRepository) - - describe("close") { - it("당일 정기모임을 찾아 pending 출석을 close") { - val now = LocalDate.now() - val targetSession = createOneDaySession(now, 1, 1111, "Today") - val otherSession = createOneDaySession(now.minusDays(1), 1, 9999, "Yesterday") - - val pendingAttendance = mockk(relaxUnitFun = true) - val attendedAttendance = mockk(relaxUnitFun = true) - val pendingUser = mockk(relaxUnitFun = true) - - every { pendingAttendance.isPending() } returns true - every { pendingAttendance.user } returns pendingUser - every { attendedAttendance.isPending() } returns false - - every { meetingGetService.find(1) } returns listOf(targetSession, otherSession) - every { - attendanceRepository.findAllBySessionAndUserStatus(targetSession, Status.ACTIVE) - } returns listOf(pendingAttendance, attendedAttendance) - - useCase.close(now, 1) - - verify { pendingAttendance.close() } - verify { pendingUser.absent() } - verify(exactly = 0) { attendedAttendance.close() } - } - - it("당일 정기모임이 없으면 MeetingNotFoundException") { - val now = LocalDate.now() - val otherDaySession = createOneDaySession(now.minusDays(1), 1, 9999, "Yesterday") - - every { meetingGetService.find(1) } returns listOf(otherDaySession) - - shouldThrow { - useCase.close(now, 1) - } - } - } - }) diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/UpdateAttendanceStatusUseCaseTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/UpdateAttendanceStatusUseCaseTest.kt deleted file mode 100644 index ebbecb4e..00000000 --- a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/command/UpdateAttendanceStatusUseCaseTest.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.weeth.domain.attendance.application.usecase.command - -import com.weeth.domain.attendance.application.dto.request.UpdateAttendanceStatusRequest -import com.weeth.domain.attendance.application.exception.AttendanceNotFoundException -import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.user.domain.entity.User -import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.DescribeSpec -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify - -class UpdateAttendanceStatusUseCaseTest : - DescribeSpec({ - - val attendanceRepository = mockk() - - val useCase = UpdateAttendanceStatusUseCase(attendanceRepository) - - describe("updateStatus") { - context("ABSENT로 변경 시") { - it("close + removeAttend + absent 호출") { - val user = mockk(relaxUnitFun = true) - val attendance = mockk(relaxUnitFun = true) - every { attendance.user } returns user - - every { attendanceRepository.findByIdWithUser(1L) } returns attendance - - val request = UpdateAttendanceStatusRequest(attendanceId = 1L, status = "ABSENT") - useCase.updateStatus(listOf(request)) - - verify { attendance.close() } - verify { user.removeAttend() } - verify { user.absent() } - } - } - - context("ATTEND로 변경 시") { - it("attend + removeAbsent + attend 호출") { - val user = mockk(relaxUnitFun = true) - val attendance = mockk(relaxUnitFun = true) - every { attendance.user } returns user - - every { attendanceRepository.findByIdWithUser(1L) } returns attendance - - val request = UpdateAttendanceStatusRequest(attendanceId = 1L, status = "ATTEND") - useCase.updateStatus(listOf(request)) - - verify { attendance.attend() } - verify { user.removeAbsent() } - verify { user.attend() } - } - } - - context("출석 정보가 없을 때") { - it("AttendanceNotFoundException") { - every { attendanceRepository.findByIdWithUser(999L) } returns null - - val request = UpdateAttendanceStatusRequest(attendanceId = 999L, status = "ABSENT") - - shouldThrow { - useCase.updateStatus(listOf(request)) - } - } - } - } - }) diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt index d6af4854..dec774aa 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt @@ -8,8 +8,8 @@ import com.weeth.domain.attendance.application.mapper.AttendanceMapper import com.weeth.domain.attendance.domain.entity.Attendance import com.weeth.domain.attendance.domain.entity.Session import com.weeth.domain.attendance.domain.repository.AttendanceRepository +import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUser -import com.weeth.domain.schedule.domain.service.MeetingGetService import com.weeth.domain.user.domain.entity.Cardinal import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.service.UserCardinalGetService @@ -19,13 +19,14 @@ import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import io.mockk.verify +import java.util.Optional class GetAttendanceQueryServiceTest : DescribeSpec({ val userGetService = mockk() val userCardinalGetService = mockk() - val meetingGetService = mockk() + val sessionRepository = mockk() val attendanceRepository = mockk() val attendanceMapper = mockk() @@ -33,7 +34,7 @@ class GetAttendanceQueryServiceTest : GetAttendanceQueryService( userGetService, userCardinalGetService, - meetingGetService, + sessionRepository, attendanceRepository, attendanceMapper, ) @@ -112,7 +113,7 @@ class GetAttendanceQueryServiceTest : val response1 = mockk() val response2 = mockk() - every { meetingGetService.find(sessionId) } returns session + every { sessionRepository.findById(sessionId) } returns Optional.of(session) every { attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) } returns listOf(attendance1, attendance2) diff --git a/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt b/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt deleted file mode 100644 index 7831eb15..00000000 --- a/src/test/kotlin/com/weeth/domain/attendance/domain/service/AttendanceSaveServiceTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.weeth.domain.attendance.domain.service - -import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUser -import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createOneDaySession -import io.kotest.core.spec.style.DescribeSpec -import io.kotest.matchers.collections.shouldHaveSize -import io.kotest.matchers.shouldBe -import io.mockk.every -import io.mockk.mockk -import io.mockk.slot -import io.mockk.verify -import java.time.LocalDate - -class AttendanceSaveServiceTest : - DescribeSpec({ - - val attendanceRepository = mockk() - val attendanceSaveService = AttendanceSaveService(attendanceRepository) - - describe("init") { - it("각 정기모임에 대한 Attendance를 생성하여 저장한다") { - val user = createActiveUser("이지훈") - val sessionFirst = createOneDaySession(LocalDate.now(), 1, 1234, "1차") - val sessionSecond = createOneDaySession(LocalDate.now().plusDays(7), 1, 5678, "2차") - - every { attendanceRepository.save(any()) } answers { firstArg() } - - attendanceSaveService.init(user, listOf(sessionFirst, sessionSecond)) - - verify(exactly = 2) { attendanceRepository.save(any()) } - } - } - - describe("saveAll") { - it("사용자 수만큼 Attendance 생성 후 saveAll 호출") { - val session = createOneDaySession(LocalDate.now(), 1, 1234, "1차") - val userFirst = createActiveUser("이지훈") - val userSecond = createActiveUser("이강혁") - - val listSlot = slot>() - every { attendanceRepository.saveAll(capture(listSlot)) } answers { firstArg() } - - attendanceSaveService.saveAll(listOf(userFirst, userSecond), session) - - val savedAttendances = listSlot.captured - savedAttendances shouldHaveSize 2 - savedAttendances.forEach { it.session shouldBe session } - savedAttendances.map { it.user } shouldBe listOf(userFirst, userSecond) - } - } - }) diff --git a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt deleted file mode 100644 index cab5e472..00000000 --- a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/MeetingUseCaseImplTest.kt +++ /dev/null @@ -1,209 +0,0 @@ -package com.weeth.domain.schedule.application.usecase - -import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.Session -import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus -import com.weeth.domain.attendance.domain.service.AttendanceDeleteService -import com.weeth.domain.attendance.domain.service.AttendanceGetService -import com.weeth.domain.attendance.domain.service.AttendanceSaveService -import com.weeth.domain.attendance.domain.service.AttendanceUpdateService -import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest -import com.weeth.domain.schedule.application.dto.response.SessionInfoResponse -import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse -import com.weeth.domain.schedule.application.dto.response.SessionResponse -import com.weeth.domain.schedule.application.mapper.SessionMapper -import com.weeth.domain.schedule.domain.entity.enums.Type -import com.weeth.domain.schedule.domain.service.MeetingDeleteService -import com.weeth.domain.schedule.domain.service.MeetingGetService -import com.weeth.domain.schedule.domain.service.MeetingSaveService -import com.weeth.domain.schedule.domain.service.MeetingUpdateService -import com.weeth.domain.schedule.fixture.ScheduleTestFixture -import com.weeth.domain.user.domain.entity.Cardinal -import com.weeth.domain.user.domain.entity.User -import com.weeth.domain.user.domain.entity.enums.Role -import com.weeth.domain.user.domain.service.CardinalGetService -import com.weeth.domain.user.domain.service.UserGetService -import io.kotest.core.spec.style.DescribeSpec -import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe -import io.mockk.clearMocks -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import jakarta.persistence.EntityManager -import org.springframework.test.util.ReflectionTestUtils -import java.time.LocalDateTime - -class MeetingUseCaseImplTest : - DescribeSpec({ - val meetingGetService = mockk() - val sessionMapper = mockk() - val meetingSaveService = mockk(relaxUnitFun = true) - val userGetService = mockk() - val meetingUpdateService = mockk(relaxUnitFun = true) - val meetingDeleteService = mockk(relaxUnitFun = true) - val attendanceGetService = mockk() - val attendanceSaveService = mockk(relaxUnitFun = true) - val attendanceDeleteService = mockk(relaxUnitFun = true) - val attendanceUpdateService = mockk(relaxUnitFun = true) - val cardinalGetService = mockk() - val em = mockk(relaxUnitFun = true) - - val useCase = - MeetingUseCaseImpl( - meetingGetService, - sessionMapper, - meetingSaveService, - userGetService, - meetingUpdateService, - meetingDeleteService, - attendanceGetService, - attendanceSaveService, - attendanceDeleteService, - attendanceUpdateService, - cardinalGetService, - ) - - beforeSpec { - ReflectionTestUtils.setField(useCase, "em", em) - } - - beforeTest { - clearMocks( - meetingGetService, - sessionMapper, - meetingSaveService, - userGetService, - meetingUpdateService, - meetingDeleteService, - attendanceGetService, - attendanceSaveService, - attendanceDeleteService, - attendanceUpdateService, - cardinalGetService, - em, - ) - } - - describe("find(userId, sessionId)") { - val sessionId = 1L - val userId = 10L - val session = ScheduleTestFixture.createSession(id = sessionId) - - context("ADMIN 유저일 때") { - it("toAdminResponse로 매핑한다") { - val adminUser = mockk() - every { adminUser.role } returns Role.ADMIN - every { userGetService.find(userId) } returns adminUser - every { meetingGetService.find(sessionId) } returns session - val adminResponse = mockk() - every { sessionMapper.toAdminResponse(session) } returns adminResponse - - val result = useCase.find(userId, sessionId) - - result shouldBe adminResponse - verify { sessionMapper.toAdminResponse(session) } - } - } - - context("일반 유저일 때") { - it("toResponse(session)으로 매핑한다 (코드 미노출)") { - val normalUser = mockk() - every { normalUser.role } returns Role.USER - every { userGetService.find(userId) } returns normalUser - every { meetingGetService.find(sessionId) } returns session - val normalResponse = mockk() - every { sessionMapper.toResponse(session) } returns normalResponse - - val result = useCase.find(userId, sessionId) - - result shouldBe normalResponse - verify { sessionMapper.toResponse(session) } - } - } - } - - describe("find(cardinal)") { - it("이번 주 세션이 있으면 thisWeek에 포함된다") { - val now = LocalDateTime.now() - val thisWeekSession = - ScheduleTestFixture.createSession( - id = 1L, - title = "This Week", - start = now, - end = now.plusHours(2), - ) - val lastWeekSession = - ScheduleTestFixture.createSession( - id = 2L, - title = "Last Week", - start = now.minusDays(14), - end = now.minusDays(14).plusHours(2), - ) - val thisWeekInfo = SessionInfoResponse(1L, 1, "This Week", now) - val lastWeekInfo = SessionInfoResponse(2L, 1, "Last Week", now.minusDays(14)) - val expectedInfos = SessionInfosResponse(thisWeekInfo, listOf(thisWeekInfo, lastWeekInfo)) - - every { meetingGetService.findMeetingByCardinal(1) } returns listOf(thisWeekSession, lastWeekSession) - every { sessionMapper.toInfos(thisWeekSession, any()) } returns expectedInfos - - val result = useCase.find(1) - - result.thisWeek shouldNotBe null - result.thisWeek!!.title shouldBe "This Week" - } - } - - describe("save") { - it("세션 저장 후 해당 기수 전체 유저에게 출석을 생성한다") { - val userId = 10L - val user = mockk() - val cardinal = mockk() - val userList = listOf(mockk(), mockk(), mockk()) - val session = ScheduleTestFixture.createSession() - val dto = - ScheduleSaveRequest( - title = "Title", - content = "Content", - location = "Location", - type = Type.MEETING, - cardinal = 1, - start = LocalDateTime.of(2026, 3, 1, 10, 0), - end = LocalDateTime.of(2026, 3, 1, 12, 0), - ) - - every { userGetService.find(userId) } returns user - every { cardinalGetService.findByUserSide(1) } returns cardinal - every { userGetService.findAllByCardinal(cardinal) } returns userList - every { sessionMapper.toEntity(dto, user) } returns session - - useCase.save(dto, userId) - - verify(exactly = 1) { meetingSaveService.save(session) } - verify(exactly = 1) { attendanceSaveService.saveAll(userList, session) } - } - } - - describe("delete") { - it("출석 통계 롤백 후 출석 삭제 → 세션 삭제 순서로 처리한다") { - val sessionId = 1L - val session = ScheduleTestFixture.createSession(id = sessionId) - val attendance1 = mockk() - val attendance2 = mockk() - val attendances = listOf(attendance1, attendance2) - - every { meetingGetService.find(sessionId) } returns session - every { attendanceGetService.findAllByMeeting(session) } returns attendances - - useCase.delete(sessionId) - - verify(ordering = io.mockk.Ordering.ORDERED) { - attendanceUpdateService.updateUserAttendanceByStatus(attendances) - em.flush() - em.clear() - attendanceDeleteService.deleteAll(session) - meetingDeleteService.delete(session) - } - } - } - }) diff --git a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt b/src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt deleted file mode 100644 index d0b3c8ee..00000000 --- a/src/test/kotlin/com/weeth/domain/schedule/application/usecase/ScheduleUseCaseImplTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.weeth.domain.schedule.application.usecase - -import com.weeth.domain.schedule.application.dto.response.ScheduleResponse -import com.weeth.domain.schedule.domain.service.EventGetService -import com.weeth.domain.schedule.domain.service.MeetingGetService -import com.weeth.domain.user.domain.service.CardinalGetService -import io.kotest.core.spec.style.DescribeSpec -import io.kotest.matchers.shouldBe -import io.mockk.clearMocks -import io.mockk.every -import io.mockk.mockk -import java.time.LocalDateTime - -class ScheduleUseCaseImplTest : - DescribeSpec({ - val eventGetService = mockk() - val meetingGetService = mockk() - val cardinalGetService = mockk() - val useCase = ScheduleUseCaseImpl(eventGetService, meetingGetService, cardinalGetService) - - beforeTest { - clearMocks(eventGetService, meetingGetService, cardinalGetService) - } - - describe("findByMonthly") { - val start = LocalDateTime.of(2026, 3, 1, 0, 0) - val end = LocalDateTime.of(2026, 3, 31, 23, 59) - - it("Event와 Meeting을 합쳐서 start 기준 오름차순 정렬한다") { - val event1 = ScheduleResponse(1L, "Event", start.plusDays(2), start.plusDays(3), false) - val meeting1 = ScheduleResponse(2L, "Meeting", start.plusDays(1), start.plusDays(1), true) - - every { eventGetService.find(start, end) } returns listOf(event1) - every { meetingGetService.find(start, end) } returns listOf(meeting1) - - val result = useCase.findByMonthly(start, end) - - result.size shouldBe 2 - result[0].title shouldBe "Meeting" - result[1].title shouldBe "Event" - } - - it("Event와 Meeting 모두 없으면 빈 리스트를 반환한다") { - every { eventGetService.find(start, end) } returns emptyList() - every { meetingGetService.find(start, end) } returns emptyList() - - val result = useCase.findByMonthly(start, end) - - result.size shouldBe 0 - } - } - }) diff --git a/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt b/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt index 2a95830c..db0e2a65 100644 --- a/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt +++ b/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt @@ -1,8 +1,9 @@ package com.weeth.domain.user.application.usecase +import com.weeth.domain.attendance.domain.entity.Attendance import com.weeth.domain.attendance.domain.entity.Session -import com.weeth.domain.attendance.domain.service.AttendanceSaveService -import com.weeth.domain.schedule.domain.service.MeetingGetService +import com.weeth.domain.attendance.domain.repository.AttendanceRepository +import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.user.application.dto.request.UserRequestDto import com.weeth.domain.user.application.dto.response.UserResponseDto import com.weeth.domain.user.application.exception.InvalidUserOrderException @@ -37,8 +38,8 @@ class UserManageUseCaseTest : val userGetService = mockk() val userUpdateService = mockk(relaxUnitFun = true) val userDeleteService = mockk(relaxUnitFun = true) - val attendanceSaveService = mockk(relaxUnitFun = true) - val meetingGetService = mockk() + val attendanceRepository = mockk(relaxed = true) + val sessionRepository = mockk() val jwtRedisService = mockk(relaxUnitFun = true) val cardinalGetService = mockk() val userCardinalSaveService = mockk(relaxUnitFun = true) @@ -51,8 +52,8 @@ class UserManageUseCaseTest : userGetService, userUpdateService, userDeleteService, - attendanceSaveService, - meetingGetService, + attendanceRepository, + sessionRepository, jwtRedisService, cardinalGetService, userCardinalSaveService, @@ -140,16 +141,16 @@ class UserManageUseCaseTest : val user1 = UserTestFixture.createWaitingUser1(1L) val userIds = UserRequestDto.UserId(listOf(1L)) val cardinal = CardinalTestFixture.createCardinal(id = 1L, cardinalNumber = 8, year = 2020, semester = 2) - val meetings = listOf(mockk()) + val session = mockk() every { userGetService.findAll(userIds.userId()) } returns listOf(user1) every { userCardinalGetService.getCurrentCardinal(user1) } returns cardinal - every { meetingGetService.find(8) } returns meetings + every { sessionRepository.findAllByCardinalOrderByStartAsc(8) } returns listOf(session) useCase.accept(userIds) verify { userUpdateService.accept(user1) } - verify { attendanceSaveService.init(user1, meetings) } + verify { attendanceRepository.saveAll(any>()) } } } @@ -203,17 +204,17 @@ class UserManageUseCaseTest : .build() val nextCardinal = CardinalTestFixture.createCardinal(id = 1L, cardinalNumber = 4, year = 2020, semester = 2) val request = UserRequestDto.UserApplyOB(1L, 4) - val meeting = listOf(mockk()) + val session = mockk() every { userGetService.find(1L) } returns user every { cardinalGetService.findByAdminSide(4) } returns nextCardinal every { userCardinalGetService.notContains(user, nextCardinal) } returns true every { userCardinalGetService.isCurrent(user, nextCardinal) } returns true - every { meetingGetService.find(4) } returns meeting + every { sessionRepository.findAllByCardinalOrderByStartAsc(4) } returns listOf(session) useCase.applyOB(listOf(request)) - verify { attendanceSaveService.init(user, meeting) } + verify { attendanceRepository.saveAll(any>()) } verify { userCardinalSaveService.save(any()) } } } From f9dcd482029a980dc4cf6df07a516f72bae49e64 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 11:54:59 +0900 Subject: [PATCH 31/62] =?UTF-8?q?refactor:=20SessionRepository/AttendanceR?= =?UTF-8?q?epository=20=EB=B9=84=EA=B4=80=EC=A0=81=20=EB=9D=BD=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/AttendanceRepository.kt | 13 +++++++++++++ .../domain/repository/SessionRepository.kt | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt index 81b6a8cf..ca914209 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt @@ -2,15 +2,28 @@ package com.weeth.domain.attendance.domain.repository import com.weeth.domain.attendance.domain.entity.Attendance import com.weeth.domain.attendance.domain.entity.Session +import com.weeth.domain.user.domain.entity.User import com.weeth.domain.user.domain.entity.enums.Status +import jakarta.persistence.LockModeType +import jakarta.persistence.QueryHint import org.springframework.data.jpa.repository.EntityGraph import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Lock import org.springframework.data.jpa.repository.Modifying import org.springframework.data.jpa.repository.Query +import org.springframework.data.jpa.repository.QueryHints import org.springframework.data.repository.query.Param import java.time.LocalDateTime interface AttendanceRepository : JpaRepository { + @Lock(LockModeType.PESSIMISTIC_WRITE) + @QueryHints(QueryHint(name = "jakarta.persistence.lock.timeout", value = "2000")) + @Query("SELECT a FROM Attendance a JOIN FETCH a.user WHERE a.session = :session AND a.user = :user") + fun findBySessionAndUserWithLock( + @Param("session") session: Session, + @Param("user") user: User, + ): Attendance? + @EntityGraph(attributePaths = ["user"]) fun findAllBySessionAndUserStatus( session: Session, diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt index 12e700b2..fb64c844 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt @@ -2,12 +2,22 @@ package com.weeth.domain.attendance.domain.repository import com.weeth.domain.attendance.domain.entity.Session import com.weeth.domain.attendance.domain.entity.enums.SessionStatus +import jakarta.persistence.LockModeType +import jakarta.persistence.QueryHint import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Lock +import org.springframework.data.jpa.repository.Query +import org.springframework.data.jpa.repository.QueryHints import java.time.LocalDateTime interface SessionRepository : JpaRepository, SessionReader { + @Lock(LockModeType.PESSIMISTIC_WRITE) + @QueryHints(QueryHint(name = "jakarta.persistence.lock.timeout", value = "2000")) + @Query("SELECT s FROM Session s WHERE s.id = :id") + fun findByIdWithLock(id: Long): Session? + fun findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc( end: LocalDateTime, start: LocalDateTime, From 744dafdf3e605c5d1dbdbf12a6c371dede17f055 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 11:55:14 +0900 Subject: [PATCH 32/62] =?UTF-8?q?refactor:=20ManageSessionUseCase/ManageAt?= =?UTF-8?q?tendanceUseCase=20=EB=B9=84=EA=B4=80=EC=A0=81=20=EB=9D=BD=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/command/ManageAttendanceUseCase.kt | 7 +++++-- .../application/usecase/command/ManageSessionUseCase.kt | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt index b454291e..0cc25838 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt @@ -35,8 +35,11 @@ class ManageAttendanceUseCase( if (todayAttendance.isWrong(code)) { throw AttendanceCodeMismatchException() } - if (todayAttendance.status != AttendanceStatus.ATTEND) { - todayAttendance.attend() + val lockedAttendance = + attendanceRepository.findBySessionAndUserWithLock(todayAttendance.session, user) + ?: throw AttendanceNotFoundException() + if (lockedAttendance.status != AttendanceStatus.ATTEND) { + lockedAttendance.attend() user.attend() } } diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt index dd9ebe81..b92314b6 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt @@ -47,14 +47,14 @@ class ManageSessionUseCase( request: ScheduleUpdateRequest, userId: Long, ) { - val session = sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException() } + val session = sessionRepository.findByIdWithLock(sessionId) ?: throw MeetingNotFoundException() val user = userGetService.find(userId) session.updateInfo(request.title, request.content, request.location, request.start, request.end, user) } @Transactional fun delete(sessionId: Long) { - val session = sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException() } + val session = sessionRepository.findByIdWithLock(sessionId) ?: throw MeetingNotFoundException() val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) attendances.forEach { a -> when (a.status) { From f2e53a2b60d56ffb4c1dccd2f2a7e0d837bb0da5 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 12:37:10 +0900 Subject: [PATCH 33/62] =?UTF-8?q?refactor:=20schedule/attendance=20?= =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20Kotlin=20=EC=A0=84?= =?UTF-8?q?=ED=99=98=20=EB=B0=8F=20Event/Session=20API=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/exception/EventErrorCode.java | 19 ------ .../exception/EventNotFoundException.java | 9 --- .../exception/MeetingErrorCode.java | 19 ------ .../exception/MeetingNotFoundException.java | 7 -- .../presentation/EventAdminController.java | 64 ----------------- .../presentation/EventController.java | 34 ---------- .../presentation/MeetingAdminController.java | 32 --------- .../presentation/MeetingController.java | 35 ---------- .../presentation/ScheduleController.java | 49 ------------- .../presentation/ScheduleResponseCode.java | 36 ---------- .../presentation/AttendanceAdminController.kt | 20 ++---- .../presentation/AttendanceController.kt | 6 +- .../presentation/AttendanceResponseCode.kt | 8 +++ .../presentation/SessionAdminController.kt | 68 +++++++++++++++++++ .../presentation/SessionController.kt | 31 +++++++++ .../application/exception/EventErrorCode.kt | 21 ++++++ .../exception/EventNotFoundException.kt | 5 ++ .../application/exception/MeetingErrorCode.kt | 21 ++++++ .../exception/MeetingNotFoundException.kt | 5 ++ .../presentation/EventAdminController.kt | 58 ++++++++++++++++ .../schedule/presentation/EventController.kt | 27 ++++++++ .../presentation/ScheduleController.kt | 40 +++++++++++ .../presentation/ScheduleResponseCode.kt | 17 +++++ 23 files changed, 309 insertions(+), 322 deletions(-) delete mode 100644 src/main/java/com/weeth/domain/schedule/application/exception/EventErrorCode.java delete mode 100644 src/main/java/com/weeth/domain/schedule/application/exception/EventNotFoundException.java delete mode 100644 src/main/java/com/weeth/domain/schedule/application/exception/MeetingErrorCode.java delete mode 100644 src/main/java/com/weeth/domain/schedule/application/exception/MeetingNotFoundException.java delete mode 100644 src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java delete mode 100644 src/main/java/com/weeth/domain/schedule/presentation/EventController.java delete mode 100644 src/main/java/com/weeth/domain/schedule/presentation/MeetingAdminController.java delete mode 100644 src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java delete mode 100644 src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java delete mode 100644 src/main/java/com/weeth/domain/schedule/presentation/ScheduleResponseCode.java create mode 100644 src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt create mode 100644 src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/exception/EventErrorCode.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/exception/EventNotFoundException.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingErrorCode.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingNotFoundException.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/presentation/EventAdminController.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleController.kt create mode 100644 src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleResponseCode.kt diff --git a/src/main/java/com/weeth/domain/schedule/application/exception/EventErrorCode.java b/src/main/java/com/weeth/domain/schedule/application/exception/EventErrorCode.java deleted file mode 100644 index 591dbaca..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/exception/EventErrorCode.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.weeth.domain.schedule.application.exception; - -import com.weeth.global.common.exception.ErrorCodeInterface; -import com.weeth.global.common.exception.ExplainError; -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -@AllArgsConstructor -public enum EventErrorCode implements ErrorCodeInterface { - - @ExplainError("요청한 일정 ID에 해당하는 일정이 존재하지 않을 때 발생합니다.") - EVENT_NOT_FOUND(2700, HttpStatus.NOT_FOUND, "존재하지 않는 일정입니다."); - - private final int code; - private final HttpStatus status; - private final String message; -} diff --git a/src/main/java/com/weeth/domain/schedule/application/exception/EventNotFoundException.java b/src/main/java/com/weeth/domain/schedule/application/exception/EventNotFoundException.java deleted file mode 100644 index 48856ea5..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/exception/EventNotFoundException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.weeth.domain.schedule.application.exception; - -import com.weeth.global.common.exception.BaseException; - -public class EventNotFoundException extends BaseException { - public EventNotFoundException() { - super(EventErrorCode.EVENT_NOT_FOUND); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/application/exception/MeetingErrorCode.java b/src/main/java/com/weeth/domain/schedule/application/exception/MeetingErrorCode.java deleted file mode 100644 index ad96ff71..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/exception/MeetingErrorCode.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.weeth.domain.schedule.application.exception; - -import com.weeth.global.common.exception.ErrorCodeInterface; -import com.weeth.global.common.exception.ExplainError; -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -@AllArgsConstructor -public enum MeetingErrorCode implements ErrorCodeInterface { - - @ExplainError("요청한 정기모임 ID에 해당하는 정기모임이 존재하지 않을 때 발생합니다.") - MEETING_NOT_FOUND(2701, HttpStatus.NOT_FOUND, "존재하지 않는 정기모임입니다."); - - private final int code; - private final HttpStatus status; - private final String message; -} diff --git a/src/main/java/com/weeth/domain/schedule/application/exception/MeetingNotFoundException.java b/src/main/java/com/weeth/domain/schedule/application/exception/MeetingNotFoundException.java deleted file mode 100644 index d9b4d4d7..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/exception/MeetingNotFoundException.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.weeth.domain.schedule.application.exception; - -import com.weeth.global.common.exception.BaseException; - -public class MeetingNotFoundException extends BaseException { - public MeetingNotFoundException() {super(MeetingErrorCode.MEETING_NOT_FOUND);} -} diff --git a/src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java b/src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java deleted file mode 100644 index 88197b03..00000000 --- a/src/main/java/com/weeth/domain/schedule/presentation/EventAdminController.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.weeth.domain.schedule.presentation; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest; -import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest; -import com.weeth.domain.schedule.application.exception.EventErrorCode; -import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase; -import com.weeth.domain.schedule.application.usecase.command.ManageEventUseCase; -import com.weeth.domain.schedule.domain.entity.enums.Type; -import com.weeth.global.auth.annotation.CurrentUser; -import com.weeth.global.common.exception.ApiErrorCodeExample; -import com.weeth.global.common.response.CommonResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; - -import static com.weeth.domain.schedule.presentation.ScheduleResponseCode.*; - -@Tag(name = "EVENT ADMIN", description = "[ADMIN] 일정 어드민 API") -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/v1/admin/events") -@ApiErrorCodeExample(EventErrorCode.class) -public class EventAdminController { - - private final ManageEventUseCase manageEventUseCase; - private final ManageSessionUseCase manageSessionUseCase; - - @PostMapping - @Operation(summary = "일정/정기모임 생성") - public CommonResponse save(@Valid @RequestBody ScheduleSaveRequest dto, - @Parameter(hidden = true) @CurrentUser Long userId) { - if (dto.getType() == Type.EVENT) { - manageEventUseCase.create(dto, userId); - } else { - manageSessionUseCase.create(dto, userId); - } - - return CommonResponse.success(EVENT_SAVE_SUCCESS); - } - - @PatchMapping("/{eventId}") - @Operation(summary = "일정 수정 (type은 변경할 수 없게 해주세요.)") - public CommonResponse update(@PathVariable Long eventId, @Valid @RequestBody ScheduleUpdateRequest dto, - @Parameter(hidden = true) @CurrentUser Long userId) { - if (dto.getType() == Type.EVENT) { - manageEventUseCase.update(eventId, dto, userId); - } else { - manageSessionUseCase.update(eventId, dto, userId); - } - - return CommonResponse.success(EVENT_UPDATE_SUCCESS); - } - - @DeleteMapping("/{eventId}") - @Operation(summary = "일정 삭제") - public CommonResponse delete(@PathVariable Long eventId) { - manageEventUseCase.delete(eventId); - - return CommonResponse.success(EVENT_DELETE_SUCCESS); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/presentation/EventController.java b/src/main/java/com/weeth/domain/schedule/presentation/EventController.java deleted file mode 100644 index d73f0c78..00000000 --- a/src/main/java/com/weeth/domain/schedule/presentation/EventController.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.weeth.domain.schedule.presentation; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import com.weeth.domain.schedule.application.dto.response.EventResponse; -import com.weeth.domain.schedule.application.exception.EventErrorCode; -import com.weeth.domain.schedule.application.usecase.command.ManageEventUseCase; -import com.weeth.global.common.exception.ApiErrorCodeExample; -import com.weeth.global.common.response.CommonResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import static com.weeth.domain.schedule.presentation.ScheduleResponseCode.EVENT_FIND_SUCCESS; - -@Tag(name = "EVENT", description = "일정 API") -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/v1/events") -@ApiErrorCodeExample(EventErrorCode.class) -public class EventController { - - private final ManageEventUseCase manageEventUseCase; - - @GetMapping("/{eventId}") - @Operation(summary="일정 상세 조회") - public CommonResponse find(@PathVariable Long eventId) { - return CommonResponse.success(EVENT_FIND_SUCCESS, - manageEventUseCase.find(eventId)); - } - -} diff --git a/src/main/java/com/weeth/domain/schedule/presentation/MeetingAdminController.java b/src/main/java/com/weeth/domain/schedule/presentation/MeetingAdminController.java deleted file mode 100644 index 15f32dbc..00000000 --- a/src/main/java/com/weeth/domain/schedule/presentation/MeetingAdminController.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.weeth.domain.schedule.presentation; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import com.weeth.domain.schedule.application.exception.MeetingErrorCode; -import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase; -import com.weeth.global.common.exception.ApiErrorCodeExample; -import com.weeth.global.common.response.CommonResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import static com.weeth.domain.schedule.presentation.ScheduleResponseCode.MEETING_DELETE_SUCCESS; - -@Tag(name = "MEETING ADMIN", description = "[ADMIN] 정기모임 어드민 API") -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/v1/admin/meetings") -@ApiErrorCodeExample(MeetingErrorCode.class) -public class MeetingAdminController { - - private final ManageSessionUseCase manageSessionUseCase; - - @DeleteMapping("/{meetingId}") - @Operation(summary = "정기모임 삭제") - public CommonResponse delete(@PathVariable Long meetingId) { - manageSessionUseCase.delete(meetingId); - return CommonResponse.success(MEETING_DELETE_SUCCESS); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java b/src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java deleted file mode 100644 index a1d680aa..00000000 --- a/src/main/java/com/weeth/domain/schedule/presentation/MeetingController.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.weeth.domain.schedule.presentation; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import com.weeth.domain.schedule.application.dto.response.SessionResponse; -import com.weeth.domain.schedule.application.exception.MeetingErrorCode; -import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase; -import com.weeth.global.auth.annotation.CurrentUser; -import com.weeth.global.common.exception.ApiErrorCodeExample; -import com.weeth.global.common.response.CommonResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import static com.weeth.domain.schedule.presentation.ScheduleResponseCode.MEETING_FIND_SUCCESS; - -@Tag(name = "MEETING", description = "정기모임 API") -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/v1/meetings") -@ApiErrorCodeExample(MeetingErrorCode.class) -public class MeetingController { - - private final ManageSessionUseCase manageSessionUseCase; - - @GetMapping("/{meetingId}") - @Operation(summary="정기모임 상세 조회") - public CommonResponse find(@Parameter(hidden = true) @CurrentUser Long userId, - @PathVariable Long meetingId) { - return CommonResponse.success(MEETING_FIND_SUCCESS, manageSessionUseCase.find(userId, meetingId)); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java b/src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java deleted file mode 100644 index 4327031d..00000000 --- a/src/main/java/com/weeth/domain/schedule/presentation/ScheduleController.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.weeth.domain.schedule.presentation; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import com.weeth.domain.schedule.application.dto.response.ScheduleResponse; -import com.weeth.domain.schedule.application.exception.EventErrorCode; -import com.weeth.domain.schedule.application.exception.MeetingErrorCode; -import com.weeth.domain.schedule.application.usecase.query.GetScheduleQueryService; -import com.weeth.global.common.exception.ApiErrorCodeExample; -import com.weeth.global.common.response.CommonResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -import static com.weeth.domain.schedule.presentation.ScheduleResponseCode.SCHEDULE_MONTHLY_FIND_SUCCESS; -import static com.weeth.domain.schedule.presentation.ScheduleResponseCode.SCHEDULE_YEARLY_FIND_SUCCESS; - -@Tag(name = "SCHEDULE", description = "캘린더 조회 API") -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/v1/schedules") -@ApiErrorCodeExample({EventErrorCode.class, MeetingErrorCode.class}) -public class ScheduleController { - - private final GetScheduleQueryService getScheduleQueryService; - - @GetMapping("/monthly") - @Operation(summary="월별 일정 조회") - public CommonResponse> findByMonthly( - @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start, - @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime end) { - return CommonResponse.success(SCHEDULE_MONTHLY_FIND_SUCCESS, getScheduleQueryService.findMonthly(start, end)); - } - - @GetMapping("/yearly") - @Operation(summary="연도별 일정 조회") - public CommonResponse>> findByYearly( - @RequestParam Integer year, - @RequestParam Integer semester) { - return CommonResponse.success(SCHEDULE_YEARLY_FIND_SUCCESS, getScheduleQueryService.findYearly(year, semester)); - } -} diff --git a/src/main/java/com/weeth/domain/schedule/presentation/ScheduleResponseCode.java b/src/main/java/com/weeth/domain/schedule/presentation/ScheduleResponseCode.java deleted file mode 100644 index 73655542..00000000 --- a/src/main/java/com/weeth/domain/schedule/presentation/ScheduleResponseCode.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.weeth.domain.schedule.presentation; - -import com.weeth.global.common.response.ResponseCodeInterface; -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -public enum ScheduleResponseCode implements ResponseCodeInterface { - // EventAdminController 관련 - EVENT_SAVE_SUCCESS(1700, HttpStatus.OK, "일정/정기모임이 성공적으로 생성되었습니다."), - EVENT_UPDATE_SUCCESS(1701, HttpStatus.OK, "일정/정기모임이 성공적으로 수정되었습니다."), - EVENT_DELETE_SUCCESS(1702, HttpStatus.OK, "일정이 성공적으로 삭제되었습니다."), - // EventController 관련 - EVENT_FIND_SUCCESS(1703, HttpStatus.OK, "일정이 성공적으로 조회되었습니다."), - // MeetingAdminController 관련 - MEETING_SAVE_SUCCESS(1704, HttpStatus.OK, "정기모임 일정이 성공적으로 생성되었습니다."), - MEETING_UPDATE_SUCCESS(1705, HttpStatus.OK, "정기모임 일정이 성공적으로 수정되었습니다."), - MEETING_DELETE_SUCCESS(1706, HttpStatus.OK, "정기모임 일정이 성공적으로 삭제되었습니다."), - MEETING_CARDINAL_FIND_SUCCESS(1707, HttpStatus.OK, "특정 기수 정기모임이 성공적으로 조회되었습니다."), - MEETING_ALL_FIND_SUCCESS(1708, HttpStatus.OK, "정기모임 전체일정이 성공적으로 조회되었습니다."), - // MeetingController 관련 - MEETING_FIND_SUCCESS(1709, HttpStatus.OK, "정기모임이 성공적으로 조회되었습니다."), - // ScheduleController 관련 - SCHEDULE_MONTHLY_FIND_SUCCESS(1710, HttpStatus.OK, "월별 일정이 성공적으로 조회되었습니다."), - SCHEDULE_YEARLY_FIND_SUCCESS(1711, HttpStatus.OK, "연도별 일정이 성공적으로 조회되었습니다."); - - private final int code; - private final HttpStatus status; - private final String message; - - ScheduleResponseCode(int code, HttpStatus status, String message) { - this.code = code; - this.status = status; - this.message = message; - } -} diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt index b8cf9204..5b033bce 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt @@ -4,9 +4,7 @@ import com.weeth.domain.attendance.application.dto.request.UpdateAttendanceStatu import com.weeth.domain.attendance.application.dto.response.AttendanceInfoResponse import com.weeth.domain.attendance.application.exception.AttendanceErrorCode import com.weeth.domain.attendance.application.usecase.command.ManageAttendanceUseCase -import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase import com.weeth.domain.attendance.application.usecase.query.GetAttendanceQueryService -import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse import com.weeth.global.common.exception.ApiErrorCodeExample import com.weeth.global.common.response.CommonResponse import io.swagger.v3.oas.annotations.Operation @@ -23,14 +21,13 @@ import java.time.LocalDate @Tag(name = "ATTENDANCE ADMIN", description = "[ADMIN] 출석 어드민 API") @RestController -@RequestMapping("/api/v1/admin/attendances") +@RequestMapping("/api/v4/admin/attendances") @ApiErrorCodeExample(AttendanceErrorCode::class) class AttendanceAdminController( private val manageAttendanceUseCase: ManageAttendanceUseCase, - private val manageSessionUseCase: ManageSessionUseCase, private val getAttendanceQueryService: GetAttendanceQueryService, ) { - @PatchMapping + @PatchMapping("/close") @Operation(summary = "출석 마감") fun close( @RequestParam now: LocalDate, @@ -40,21 +37,14 @@ class AttendanceAdminController( return CommonResponse.success(AttendanceResponseCode.ATTENDANCE_CLOSE_SUCCESS) } - @GetMapping("/meetings") - @Operation(summary = "정기모임 조회") - fun getMeetings( - @RequestParam(required = false) cardinal: Int?, - ): CommonResponse = - CommonResponse.success(AttendanceResponseCode.MEETING_FIND_SUCCESS, manageSessionUseCase.findInfos(cardinal)) - - @GetMapping("/{meetingId}") + @GetMapping("/{sessionId}") @Operation(summary = "모든 인원 정기모임 출석 정보 조회") fun getAllAttendance( - @PathVariable meetingId: Long, + @PathVariable sessionId: Long, ): CommonResponse> = CommonResponse.success( AttendanceResponseCode.ATTENDANCE_FIND_DETAIL_SUCCESS, - getAttendanceQueryService.findAllAttendanceByMeeting(meetingId), + getAttendanceQueryService.findAllAttendanceByMeeting(sessionId), ) @PatchMapping("/status") diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt index fd6c02b8..37aa0ec9 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceController.kt @@ -13,20 +13,20 @@ import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PatchMapping +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @Tag(name = "ATTENDANCE", description = "출석 API") @RestController -@RequestMapping("/api/v1/attendances") +@RequestMapping("/api/v4/attendances") @ApiErrorCodeExample(AttendanceErrorCode::class) class AttendanceController( private val manageAttendanceUseCase: ManageAttendanceUseCase, private val getAttendanceQueryService: GetAttendanceQueryService, ) { - @PatchMapping + @PostMapping("/check-in") @Operation(summary = "출석체크") fun checkIn( @Parameter(hidden = true) @CurrentUser userId: Long, diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt index 54ad6348..7d68fb7e 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt @@ -18,4 +18,12 @@ enum class AttendanceResponseCode( ATTENDANCE_CHECKIN_SUCCESS(1204, HttpStatus.OK, "출석이 성공적으로 처리되었습니다."), ATTENDANCE_FIND_SUCCESS(1205, HttpStatus.OK, "사용자의 출석 정보가 성공적으로 조회되었습니다."), ATTENDANCE_FIND_ALL_SUCCESS(1206, HttpStatus.OK, "사용자의 상세 출석 정보가 성공적으로 조회되었습니다."), + + // SessionAdminController 관련 + SESSION_SAVE_SUCCESS(1207, HttpStatus.OK, "정기모임이 성공적으로 생성되었습니다."), + SESSION_UPDATE_SUCCESS(1208, HttpStatus.OK, "정기모임이 성공적으로 수정되었습니다."), + SESSION_DELETE_SUCCESS(1209, HttpStatus.OK, "정기모임이 성공적으로 삭제되었습니다."), + + // SessionController 관련 + SESSION_FIND_SUCCESS(1210, HttpStatus.OK, "정기모임이 성공적으로 조회되었습니다."), } diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt new file mode 100644 index 00000000..2a818329 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt @@ -0,0 +1,68 @@ +package com.weeth.domain.attendance.presentation + +import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest +import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest +import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse +import com.weeth.domain.schedule.application.exception.MeetingErrorCode +import com.weeth.global.auth.annotation.CurrentUser +import com.weeth.global.common.exception.ApiErrorCodeExample +import com.weeth.global.common.response.CommonResponse +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.tags.Tag +import jakarta.validation.Valid +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PatchMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@Tag(name = "SESSION ADMIN", description = "[ADMIN] 정기모임 어드민 API") +@RestController +@RequestMapping("/api/v4/admin/sessions") +@ApiErrorCodeExample(MeetingErrorCode::class) +class SessionAdminController( + private val manageSessionUseCase: ManageSessionUseCase, +) { + @PostMapping + @Operation(summary = "정기모임 생성") + fun create( + @Valid @RequestBody dto: ScheduleSaveRequest, + @Parameter(hidden = true) @CurrentUser userId: Long, + ): CommonResponse { + manageSessionUseCase.create(dto, userId) + return CommonResponse.success(AttendanceResponseCode.SESSION_SAVE_SUCCESS) + } + + @PatchMapping("/{sessionId}") + @Operation(summary = "정기모임 수정") + fun update( + @PathVariable sessionId: Long, + @Valid @RequestBody dto: ScheduleUpdateRequest, + @Parameter(hidden = true) @CurrentUser userId: Long, + ): CommonResponse { + manageSessionUseCase.update(sessionId, dto, userId) + return CommonResponse.success(AttendanceResponseCode.SESSION_UPDATE_SUCCESS) + } + + @DeleteMapping("/{sessionId}") + @Operation(summary = "정기모임 삭제") + fun delete( + @PathVariable sessionId: Long, + ): CommonResponse { + manageSessionUseCase.delete(sessionId) + return CommonResponse.success(AttendanceResponseCode.SESSION_DELETE_SUCCESS) + } + + @GetMapping + @Operation(summary = "정기모임 목록 조회") + fun findInfos( + @RequestParam(required = false) cardinal: Int?, + ): CommonResponse = + CommonResponse.success(AttendanceResponseCode.MEETING_FIND_SUCCESS, manageSessionUseCase.findInfos(cardinal)) +} diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt new file mode 100644 index 00000000..416aaaac --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt @@ -0,0 +1,31 @@ +package com.weeth.domain.attendance.presentation + +import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase +import com.weeth.domain.schedule.application.dto.response.SessionResponse +import com.weeth.domain.schedule.application.exception.MeetingErrorCode +import com.weeth.global.auth.annotation.CurrentUser +import com.weeth.global.common.exception.ApiErrorCodeExample +import com.weeth.global.common.response.CommonResponse +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@Tag(name = "SESSION", description = "정기모임 API") +@RestController +@RequestMapping("/api/v4/sessions") +@ApiErrorCodeExample(MeetingErrorCode::class) +class SessionController( + private val manageSessionUseCase: ManageSessionUseCase, +) { + @GetMapping("/{sessionId}") + @Operation(summary = "정기모임 상세 조회") + fun find( + @Parameter(hidden = true) @CurrentUser userId: Long, + @PathVariable sessionId: Long, + ): CommonResponse = + CommonResponse.success(AttendanceResponseCode.SESSION_FIND_SUCCESS, manageSessionUseCase.find(userId, sessionId)) +} diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/exception/EventErrorCode.kt b/src/main/kotlin/com/weeth/domain/schedule/application/exception/EventErrorCode.kt new file mode 100644 index 00000000..2e266bb0 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/exception/EventErrorCode.kt @@ -0,0 +1,21 @@ +package com.weeth.domain.schedule.application.exception + +import com.weeth.global.common.exception.ErrorCodeInterface +import com.weeth.global.common.exception.ExplainError +import org.springframework.http.HttpStatus + +enum class EventErrorCode( + private val code: Int, + private val status: HttpStatus, + private val message: String, +) : ErrorCodeInterface { + @ExplainError("요청한 일정 ID에 해당하는 일정이 존재하지 않을 때 발생합니다.") + EVENT_NOT_FOUND(2700, HttpStatus.NOT_FOUND, "존재하지 않는 일정입니다."), + ; + + override fun getCode(): Int = code + + override fun getStatus(): HttpStatus = status + + override fun getMessage(): String = message +} diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/exception/EventNotFoundException.kt b/src/main/kotlin/com/weeth/domain/schedule/application/exception/EventNotFoundException.kt new file mode 100644 index 00000000..56968357 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/exception/EventNotFoundException.kt @@ -0,0 +1,5 @@ +package com.weeth.domain.schedule.application.exception + +import com.weeth.global.common.exception.BaseException + +class EventNotFoundException : BaseException(EventErrorCode.EVENT_NOT_FOUND) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingErrorCode.kt b/src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingErrorCode.kt new file mode 100644 index 00000000..21427fd7 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingErrorCode.kt @@ -0,0 +1,21 @@ +package com.weeth.domain.schedule.application.exception + +import com.weeth.global.common.exception.ErrorCodeInterface +import com.weeth.global.common.exception.ExplainError +import org.springframework.http.HttpStatus + +enum class MeetingErrorCode( + private val code: Int, + private val status: HttpStatus, + private val message: String, +) : ErrorCodeInterface { + @ExplainError("요청한 정기모임 ID에 해당하는 정기모임이 존재하지 않을 때 발생합니다.") + MEETING_NOT_FOUND(2701, HttpStatus.NOT_FOUND, "존재하지 않는 정기모임입니다."), + ; + + override fun getCode(): Int = code + + override fun getStatus(): HttpStatus = status + + override fun getMessage(): String = message +} diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingNotFoundException.kt b/src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingNotFoundException.kt new file mode 100644 index 00000000..ab3ace42 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingNotFoundException.kt @@ -0,0 +1,5 @@ +package com.weeth.domain.schedule.application.exception + +import com.weeth.global.common.exception.BaseException + +class MeetingNotFoundException : BaseException(MeetingErrorCode.MEETING_NOT_FOUND) diff --git a/src/main/kotlin/com/weeth/domain/schedule/presentation/EventAdminController.kt b/src/main/kotlin/com/weeth/domain/schedule/presentation/EventAdminController.kt new file mode 100644 index 00000000..bc3c6434 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/presentation/EventAdminController.kt @@ -0,0 +1,58 @@ +package com.weeth.domain.schedule.presentation + +import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest +import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest +import com.weeth.domain.schedule.application.exception.EventErrorCode +import com.weeth.domain.schedule.application.usecase.command.ManageEventUseCase +import com.weeth.global.auth.annotation.CurrentUser +import com.weeth.global.common.exception.ApiErrorCodeExample +import com.weeth.global.common.response.CommonResponse +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.tags.Tag +import jakarta.validation.Valid +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.PatchMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@Tag(name = "EVENT ADMIN", description = "[ADMIN] 일정 어드민 API") +@RestController +@RequestMapping("/api/v4/admin/events") +@ApiErrorCodeExample(EventErrorCode::class) +class EventAdminController( + private val manageEventUseCase: ManageEventUseCase, +) { + @PostMapping + @Operation(summary = "일정 생성") + fun create( + @Valid @RequestBody dto: ScheduleSaveRequest, + @Parameter(hidden = true) @CurrentUser userId: Long, + ): CommonResponse { + manageEventUseCase.create(dto, userId) + return CommonResponse.success(ScheduleResponseCode.EVENT_SAVE_SUCCESS) + } + + @PatchMapping("/{eventId}") + @Operation(summary = "일정 수정") + fun update( + @PathVariable eventId: Long, + @Valid @RequestBody dto: ScheduleUpdateRequest, + @Parameter(hidden = true) @CurrentUser userId: Long, + ): CommonResponse { + manageEventUseCase.update(eventId, dto, userId) + return CommonResponse.success(ScheduleResponseCode.EVENT_UPDATE_SUCCESS) + } + + @DeleteMapping("/{eventId}") + @Operation(summary = "일정 삭제") + fun delete( + @PathVariable eventId: Long, + ): CommonResponse { + manageEventUseCase.delete(eventId) + return CommonResponse.success(ScheduleResponseCode.EVENT_DELETE_SUCCESS) + } +} diff --git a/src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt b/src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt new file mode 100644 index 00000000..390c9f3a --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt @@ -0,0 +1,27 @@ +package com.weeth.domain.schedule.presentation + +import com.weeth.domain.schedule.application.dto.response.EventResponse +import com.weeth.domain.schedule.application.exception.EventErrorCode +import com.weeth.domain.schedule.application.usecase.command.ManageEventUseCase +import com.weeth.global.common.exception.ApiErrorCodeExample +import com.weeth.global.common.response.CommonResponse +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@Tag(name = "EVENT", description = "일정 API") +@RestController +@RequestMapping("/api/v4/events") +@ApiErrorCodeExample(EventErrorCode::class) +class EventController( + private val manageEventUseCase: ManageEventUseCase, +) { + @GetMapping("/{eventId}") + @Operation(summary = "일정 상세 조회") + fun find( + @PathVariable eventId: Long, + ): CommonResponse = CommonResponse.success(ScheduleResponseCode.EVENT_FIND_SUCCESS, manageEventUseCase.find(eventId)) +} diff --git a/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleController.kt b/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleController.kt new file mode 100644 index 00000000..e7999833 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleController.kt @@ -0,0 +1,40 @@ +package com.weeth.domain.schedule.presentation + +import com.weeth.domain.schedule.application.dto.response.ScheduleResponse +import com.weeth.domain.schedule.application.exception.EventErrorCode +import com.weeth.domain.schedule.application.exception.MeetingErrorCode +import com.weeth.domain.schedule.application.usecase.query.GetScheduleQueryService +import com.weeth.global.common.exception.ApiErrorCodeExample +import com.weeth.global.common.response.CommonResponse +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.format.annotation.DateTimeFormat +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController +import java.time.LocalDateTime + +@Tag(name = "SCHEDULE", description = "캘린더 조회 API") +@RestController +@RequestMapping("/api/v4/schedules") +@ApiErrorCodeExample(EventErrorCode::class, MeetingErrorCode::class) +class ScheduleController( + private val getScheduleQueryService: GetScheduleQueryService, +) { + @GetMapping("/monthly") + @Operation(summary = "월별 일정 조회") + fun findByMonthly( + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) start: LocalDateTime, + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) end: LocalDateTime, + ): CommonResponse> = + CommonResponse.success(ScheduleResponseCode.SCHEDULE_MONTHLY_FIND_SUCCESS, getScheduleQueryService.findMonthly(start, end)) + + @GetMapping("/yearly") + @Operation(summary = "연도별 일정 조회") + fun findByYearly( + @RequestParam year: Int, + @RequestParam semester: Int, + ): CommonResponse>> = + CommonResponse.success(ScheduleResponseCode.SCHEDULE_YEARLY_FIND_SUCCESS, getScheduleQueryService.findYearly(year, semester)) +} diff --git a/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleResponseCode.kt b/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleResponseCode.kt new file mode 100644 index 00000000..f0efd119 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleResponseCode.kt @@ -0,0 +1,17 @@ +package com.weeth.domain.schedule.presentation + +import com.weeth.global.common.response.ResponseCodeInterface +import org.springframework.http.HttpStatus + +enum class ScheduleResponseCode( + override val code: Int, + override val status: HttpStatus, + override val message: String, +) : ResponseCodeInterface { + EVENT_SAVE_SUCCESS(1700, HttpStatus.OK, "일정이 성공적으로 생성되었습니다."), + EVENT_UPDATE_SUCCESS(1701, HttpStatus.OK, "일정이 성공적으로 수정되었습니다."), + EVENT_DELETE_SUCCESS(1702, HttpStatus.OK, "일정이 성공적으로 삭제되었습니다."), + EVENT_FIND_SUCCESS(1703, HttpStatus.OK, "일정이 성공적으로 조회되었습니다."), + SCHEDULE_MONTHLY_FIND_SUCCESS(1710, HttpStatus.OK, "월별 일정이 성공적으로 조회되었습니다."), + SCHEDULE_YEARLY_FIND_SUCCESS(1711, HttpStatus.OK, "연도별 일정이 성공적으로 조회되었습니다."), +} From 59879780e071f97b8fa392a3dd34bcd65f4dfcc5 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 12:51:54 +0900 Subject: [PATCH 34/62] =?UTF-8?q?refactor:=20ScheduleTimeCheck=20Kotlin=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=20=EB=B0=8F=20=EC=9A=94=EC=B2=AD=20DTO=20typ?= =?UTF-8?q?e=20=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annotation/ScheduleTimeCheck.java | 24 ------------------- .../annotation/ScheduleTimeCheck.kt | 15 ++++++++++++ .../dto/request/ScheduleSaveRequest.kt | 3 --- .../dto/request/ScheduleUpdateRequest.kt | 3 --- 4 files changed, 15 insertions(+), 30 deletions(-) delete mode 100644 src/main/java/com/weeth/domain/schedule/application/annotation/ScheduleTimeCheck.java create mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/annotation/ScheduleTimeCheck.kt diff --git a/src/main/java/com/weeth/domain/schedule/application/annotation/ScheduleTimeCheck.java b/src/main/java/com/weeth/domain/schedule/application/annotation/ScheduleTimeCheck.java deleted file mode 100644 index 7e8ec584..00000000 --- a/src/main/java/com/weeth/domain/schedule/application/annotation/ScheduleTimeCheck.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.weeth.domain.schedule.application.annotation; - -import jakarta.validation.Constraint; -import jakarta.validation.Payload; -import com.weeth.domain.schedule.application.validator.ScheduleTimeCheckValidator; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -@Target({FIELD}) -@Retention(RUNTIME) -@Constraint(validatedBy = ScheduleTimeCheckValidator.class) -public @interface ScheduleTimeCheck { - - String message() default "마감 시간이 시작 시간보다 빠를 수 없습니다."; - - Class[] groups() default {}; - - Class[] payload() default {}; - -} diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/annotation/ScheduleTimeCheck.kt b/src/main/kotlin/com/weeth/domain/schedule/application/annotation/ScheduleTimeCheck.kt new file mode 100644 index 00000000..46202a1d --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/schedule/application/annotation/ScheduleTimeCheck.kt @@ -0,0 +1,15 @@ +package com.weeth.domain.schedule.application.annotation + +import com.weeth.domain.schedule.application.validator.ScheduleTimeCheckValidator +import jakarta.validation.Constraint +import jakarta.validation.Payload +import kotlin.reflect.KClass + +@Target(AnnotationTarget.FIELD) +@Retention(AnnotationRetention.RUNTIME) +@Constraint(validatedBy = [ScheduleTimeCheckValidator::class]) +annotation class ScheduleTimeCheck( + val message: String = "마감 시간이 시작 시간보다 빠를 수 없습니다.", + val groups: Array> = [], + val payload: Array> = [], +) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt index 1099d68a..8103ce5e 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt @@ -1,6 +1,5 @@ package com.weeth.domain.schedule.application.dto.request -import com.weeth.domain.schedule.domain.entity.enums.Type import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotNull import org.springframework.format.annotation.DateTimeFormat @@ -14,8 +13,6 @@ data class ScheduleSaveRequest( @field:NotBlank val location: String, @field:NotNull - val type: Type, - @field:NotNull val cardinal: Int, @field:NotNull @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt index 1f4037e9..60c9543d 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt @@ -1,6 +1,5 @@ package com.weeth.domain.schedule.application.dto.request -import com.weeth.domain.schedule.domain.entity.enums.Type import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotNull import org.springframework.format.annotation.DateTimeFormat @@ -14,8 +13,6 @@ data class ScheduleUpdateRequest( @field:NotBlank val location: String, @field:NotNull - val type: Type, - @field:NotNull @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) val start: LocalDateTime, @field:NotNull From 41bb371a1daf2eb06a283611bf2c954ae965e511 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 17:39:27 +0900 Subject: [PATCH 35/62] =?UTF-8?q?=20fix:=20ScheduleTimeCheckValidator=20nu?= =?UTF-8?q?ll=20=EC=9E=85=EB=A0=A5=20=EC=8B=9C=20NPE=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/validator/ScheduleTimeCheckValidator.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt b/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt index e71e1785..84c8dc76 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/validator/ScheduleTimeCheckValidator.kt @@ -7,7 +7,7 @@ import jakarta.validation.ConstraintValidatorContext class ScheduleTimeCheckValidator : ConstraintValidator { override fun isValid( - time: ScheduleTimeRequest, + time: ScheduleTimeRequest?, context: ConstraintValidatorContext, - ): Boolean = time.start.isBefore(time.end.plusMinutes(1)) + ): Boolean = time == null || time.start.isBefore(time.end.plusMinutes(1)) } From 391442f13ded69466aba0305488ba9b3ceb6a1ec Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 17:59:53 +0900 Subject: [PATCH 36/62] =?UTF-8?q?refactor:=20Event=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EB=A5=BC=20Command=20UseCase=EC=97=90=EC=84=9C=20QueryService?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/command/ManageEventUseCase.kt | 4 ---- .../usecase/query/GetScheduleQueryService.kt | 10 ++++++++++ .../domain/schedule/presentation/EventController.kt | 8 ++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt index 63cd6d6e..d6e0c117 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt @@ -2,7 +2,6 @@ package com.weeth.domain.schedule.application.usecase.command import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest -import com.weeth.domain.schedule.application.dto.response.EventResponse import com.weeth.domain.schedule.application.exception.EventNotFoundException import com.weeth.domain.schedule.application.mapper.EventMapper import com.weeth.domain.schedule.domain.repository.EventRepository @@ -18,9 +17,6 @@ class ManageEventUseCase( private val cardinalGetService: CardinalGetService, private val eventMapper: EventMapper, ) { - fun find(eventId: Long): EventResponse = - eventMapper.toResponse(eventRepository.findById(eventId).orElseThrow { EventNotFoundException() }) - @Transactional fun create( request: ScheduleSaveRequest, diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt index d7737910..15347aaf 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt @@ -1,10 +1,14 @@ package com.weeth.domain.schedule.application.usecase.query import com.weeth.domain.attendance.domain.repository.SessionRepository +import com.weeth.domain.schedule.application.dto.response.EventResponse import com.weeth.domain.schedule.application.dto.response.ScheduleResponse +import com.weeth.domain.schedule.application.exception.EventNotFoundException +import com.weeth.domain.schedule.application.mapper.EventMapper import com.weeth.domain.schedule.application.mapper.ScheduleMapper import com.weeth.domain.schedule.domain.repository.EventRepository import com.weeth.domain.user.domain.service.CardinalGetService +import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime @@ -18,7 +22,13 @@ class GetScheduleQueryService( private val sessionRepository: SessionRepository, private val cardinalGetService: CardinalGetService, private val scheduleMapper: ScheduleMapper, + private val eventMapper: EventMapper, ) { + fun findEvent(eventId: Long): EventResponse = + eventRepository.findByIdOrNull(eventId) + ?.let { eventMapper.toResponse(it) } + ?: throw EventNotFoundException() + fun findMonthly( start: LocalDateTime, end: LocalDateTime, diff --git a/src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt b/src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt index 390c9f3a..9727e729 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt @@ -2,7 +2,7 @@ package com.weeth.domain.schedule.presentation import com.weeth.domain.schedule.application.dto.response.EventResponse import com.weeth.domain.schedule.application.exception.EventErrorCode -import com.weeth.domain.schedule.application.usecase.command.ManageEventUseCase +import com.weeth.domain.schedule.application.usecase.query.GetScheduleQueryService import com.weeth.global.common.exception.ApiErrorCodeExample import com.weeth.global.common.response.CommonResponse import io.swagger.v3.oas.annotations.Operation @@ -17,11 +17,11 @@ import org.springframework.web.bind.annotation.RestController @RequestMapping("/api/v4/events") @ApiErrorCodeExample(EventErrorCode::class) class EventController( - private val manageEventUseCase: ManageEventUseCase, + private val getScheduleQueryService: GetScheduleQueryService, ) { @GetMapping("/{eventId}") @Operation(summary = "일정 상세 조회") - fun find( + fun getEvent( @PathVariable eventId: Long, - ): CommonResponse = CommonResponse.success(ScheduleResponseCode.EVENT_FIND_SUCCESS, manageEventUseCase.find(eventId)) + ): CommonResponse = CommonResponse.success(ScheduleResponseCode.EVENT_FIND_SUCCESS, getScheduleQueryService.findEvent(eventId)) } From 0fa2e87ad7a4c1f7c3d334b379192e47d50d5976 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 18:03:54 +0900 Subject: [PATCH 37/62] =?UTF-8?q?style:=20GetScheduleQueryService=20Kotlin?= =?UTF-8?q?=20=EA=B4=80=EC=9A=A9=20=ED=91=9C=ED=98=84=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/query/GetScheduleQueryService.kt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt index 15347aaf..7564419a 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt @@ -12,8 +12,7 @@ import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime -import java.util.AbstractMap -import java.util.stream.IntStream + @Service @Transactional(readOnly = true) @@ -61,10 +60,7 @@ class GetScheduleQueryService( return (events + sessions) .sortedBy { it.start } .flatMap { schedule -> - IntStream - .range(schedule.start.monthValue, schedule.end.monthValue + 1) - .mapToObj { month -> AbstractMap.SimpleEntry(month, schedule) } - .toList() - }.groupBy({ it.key }, { it.value }) + (schedule.start.monthValue..schedule.end.monthValue).map { month -> month to schedule } + }.groupBy({ it.first }, { it.second }) } } From ac139e7f1aab149d7b155e584b8412e88d3d61af Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 18:07:02 +0900 Subject: [PATCH 38/62] =?UTF-8?q?style:=20findById().orElseThrow()=20Kotli?= =?UTF-8?q?n=20=EC=8A=A4=ED=83=80=EC=9D=BC=EB=A1=9C=20=ED=86=B5=EC=9D=BC?= =?UTF-8?q?=20=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A4=84?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/command/ManageEventUseCase.kt | 5 +++-- .../application/usecase/query/GetScheduleQueryService.kt | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt index d6e0c117..484d1697 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/command/ManageEventUseCase.kt @@ -7,6 +7,7 @@ import com.weeth.domain.schedule.application.mapper.EventMapper import com.weeth.domain.schedule.domain.repository.EventRepository import com.weeth.domain.user.domain.service.CardinalGetService import com.weeth.domain.user.domain.service.UserGetService +import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -34,13 +35,13 @@ class ManageEventUseCase( userId: Long, ) { val user = userGetService.find(userId) - val event = eventRepository.findById(eventId).orElseThrow { EventNotFoundException() } + val event = eventRepository.findByIdOrNull(eventId) ?: throw EventNotFoundException() event.update(request.title, request.content, request.location, request.start, request.end, user) } @Transactional fun delete(eventId: Long) { - val event = eventRepository.findById(eventId).orElseThrow { EventNotFoundException() } + val event = eventRepository.findByIdOrNull(eventId) ?: throw EventNotFoundException() eventRepository.delete(event) } } diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt index 7564419a..ff0a3465 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt @@ -13,7 +13,6 @@ import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime - @Service @Transactional(readOnly = true) class GetScheduleQueryService( From c424640055f29034a34a09ab84c80fdf432d5a84 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 18:20:31 +0900 Subject: [PATCH 39/62] =?UTF-8?q?refactor:=20Session=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EB=A5=BC=20Command=20UseCase=EC=97=90=EC=84=9C=20GetSessionQue?= =?UTF-8?q?ryService=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/command/ManageSessionUseCase.kt | 43 +------------ .../usecase/query/GetSessionQueryService.kt | 61 +++++++++++++++++++ .../presentation/SessionAdminController.kt | 6 +- .../presentation/SessionController.kt | 8 +-- 4 files changed, 70 insertions(+), 48 deletions(-) create mode 100644 src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt index b92314b6..3727b815 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt @@ -6,19 +6,14 @@ import com.weeth.domain.attendance.domain.repository.AttendanceRepository import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest -import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse -import com.weeth.domain.schedule.application.dto.response.SessionResponse import com.weeth.domain.schedule.application.exception.MeetingNotFoundException import com.weeth.domain.schedule.application.mapper.SessionMapper -import com.weeth.domain.user.domain.entity.enums.Role import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.service.CardinalGetService import com.weeth.domain.user.domain.service.UserGetService +import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import java.time.DayOfWeek -import java.time.LocalDate -import java.time.temporal.TemporalAdjusters @Service class ManageSessionUseCase( @@ -67,40 +62,4 @@ class ManageSessionUseCase( sessionRepository.delete(session) } - fun find( - userId: Long, - sessionId: Long, - ): SessionResponse { - val user = userGetService.find(userId) - val session = sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException() } - return if (user.role == Role.ADMIN) { - sessionMapper.toAdminResponse(session) - } else { - sessionMapper.toResponse(session) - } - } - - fun findInfos(cardinal: Int?): SessionInfosResponse { - val sessions = - if (cardinal == null) { - sessionRepository.findAllByOrderByStartDesc() - } else { - sessionRepository.findAllByCardinalOrderByStartDesc(cardinal) - } - val thisWeek = findThisWeek(sessions) - val sorted = sessions.sortedByDescending { it.start } - return sessionMapper.toInfos(thisWeek, sorted) - } - - private fun findThisWeek( - sessions: List, - ): com.weeth.domain.attendance.domain.entity.Session? { - val today = LocalDate.now() - val startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) - val endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)) - return sessions.firstOrNull { s -> - val d = s.start.toLocalDate() - !d.isBefore(startOfWeek) && !d.isAfter(endOfWeek) - } - } } diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt new file mode 100644 index 00000000..7e03e488 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt @@ -0,0 +1,61 @@ +package com.weeth.domain.attendance.application.usecase.query + +import com.weeth.domain.attendance.domain.repository.SessionRepository +import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse +import com.weeth.domain.schedule.application.dto.response.SessionResponse +import com.weeth.domain.schedule.application.exception.MeetingNotFoundException +import com.weeth.domain.schedule.application.mapper.SessionMapper +import com.weeth.domain.user.domain.entity.enums.Role +import com.weeth.domain.user.domain.service.UserGetService +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import com.weeth.domain.attendance.domain.entity.Session +import java.time.DayOfWeek +import java.time.LocalDate +import java.time.temporal.TemporalAdjusters + +@Service +@Transactional(readOnly = true) +class GetSessionQueryService( + private val sessionRepository: SessionRepository, + private val userGetService: UserGetService, + private val sessionMapper: SessionMapper, +) { + fun findSession( + userId: Long, + sessionId: Long, + ): SessionResponse { + val user = userGetService.find(userId) + val session = sessionRepository.findByIdOrNull(sessionId) ?: throw MeetingNotFoundException() + return if (user.role == Role.ADMIN) { + sessionMapper.toAdminResponse(session) + } else { + sessionMapper.toResponse(session) + } + } + + fun findSessionInfos(cardinal: Int?): SessionInfosResponse { + val sessions = + if (cardinal == null) { + sessionRepository.findAllByOrderByStartDesc() + } else { + sessionRepository.findAllByCardinalOrderByStartDesc(cardinal) + } + val thisWeek = findThisWeek(sessions) + val sorted = sessions.sortedByDescending { it.start } + return sessionMapper.toInfos(thisWeek, sorted) + } + + private fun findThisWeek( + sessions: List, + ): Session? { + val today = LocalDate.now() + val startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) + val endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)) + return sessions.firstOrNull { s -> + val d = s.start.toLocalDate() + !d.isBefore(startOfWeek) && !d.isAfter(endOfWeek) + } + } +} diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt index 2a818329..21959a91 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt @@ -1,6 +1,7 @@ package com.weeth.domain.attendance.presentation import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase +import com.weeth.domain.attendance.application.usecase.query.GetSessionQueryService import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse @@ -28,6 +29,7 @@ import org.springframework.web.bind.annotation.RestController @ApiErrorCodeExample(MeetingErrorCode::class) class SessionAdminController( private val manageSessionUseCase: ManageSessionUseCase, + private val getSessionQueryService: GetSessionQueryService, ) { @PostMapping @Operation(summary = "정기모임 생성") @@ -61,8 +63,8 @@ class SessionAdminController( @GetMapping @Operation(summary = "정기모임 목록 조회") - fun findInfos( + fun getSessionInfos( @RequestParam(required = false) cardinal: Int?, ): CommonResponse = - CommonResponse.success(AttendanceResponseCode.MEETING_FIND_SUCCESS, manageSessionUseCase.findInfos(cardinal)) + CommonResponse.success(AttendanceResponseCode.MEETING_FIND_SUCCESS, getSessionQueryService.findSessionInfos(cardinal)) } diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt index 416aaaac..98f60beb 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt @@ -1,6 +1,6 @@ package com.weeth.domain.attendance.presentation -import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase +import com.weeth.domain.attendance.application.usecase.query.GetSessionQueryService import com.weeth.domain.schedule.application.dto.response.SessionResponse import com.weeth.domain.schedule.application.exception.MeetingErrorCode import com.weeth.global.auth.annotation.CurrentUser @@ -19,13 +19,13 @@ import org.springframework.web.bind.annotation.RestController @RequestMapping("/api/v4/sessions") @ApiErrorCodeExample(MeetingErrorCode::class) class SessionController( - private val manageSessionUseCase: ManageSessionUseCase, + private val getSessionQueryService: GetSessionQueryService, ) { @GetMapping("/{sessionId}") @Operation(summary = "정기모임 상세 조회") - fun find( + fun getSession( @Parameter(hidden = true) @CurrentUser userId: Long, @PathVariable sessionId: Long, ): CommonResponse = - CommonResponse.success(AttendanceResponseCode.SESSION_FIND_SUCCESS, manageSessionUseCase.find(userId, sessionId)) + CommonResponse.success(AttendanceResponseCode.SESSION_FIND_SUCCESS, getSessionQueryService.findSession(userId, sessionId)) } From c82cca5ce4f39254afb83482c1c356d3ff0b148d Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 18:42:38 +0900 Subject: [PATCH 40/62] =?UTF-8?q?style:=20ktlintFormat=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/command/ManageSessionUseCase.kt | 1 - .../application/usecase/query/GetSessionQueryService.kt | 6 ++---- .../application/usecase/query/GetScheduleQueryService.kt | 3 ++- .../weeth/domain/schedule/presentation/EventController.kt | 3 ++- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt index 3727b815..ee1b4f28 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt @@ -61,5 +61,4 @@ class ManageSessionUseCase( attendanceRepository.deleteAllBySession(session) sessionRepository.delete(session) } - } diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt index 7e03e488..0d58a315 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt @@ -1,5 +1,6 @@ package com.weeth.domain.attendance.application.usecase.query +import com.weeth.domain.attendance.domain.entity.Session import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse import com.weeth.domain.schedule.application.dto.response.SessionResponse @@ -10,7 +11,6 @@ import com.weeth.domain.user.domain.service.UserGetService import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import com.weeth.domain.attendance.domain.entity.Session import java.time.DayOfWeek import java.time.LocalDate import java.time.temporal.TemporalAdjusters @@ -47,9 +47,7 @@ class GetSessionQueryService( return sessionMapper.toInfos(thisWeek, sorted) } - private fun findThisWeek( - sessions: List, - ): Session? { + private fun findThisWeek(sessions: List): Session? { val today = LocalDate.now() val startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) val endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt index ff0a3465..a2203029 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt @@ -23,7 +23,8 @@ class GetScheduleQueryService( private val eventMapper: EventMapper, ) { fun findEvent(eventId: Long): EventResponse = - eventRepository.findByIdOrNull(eventId) + eventRepository + .findByIdOrNull(eventId) ?.let { eventMapper.toResponse(it) } ?: throw EventNotFoundException() diff --git a/src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt b/src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt index 9727e729..ca6d17c1 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/presentation/EventController.kt @@ -23,5 +23,6 @@ class EventController( @Operation(summary = "일정 상세 조회") fun getEvent( @PathVariable eventId: Long, - ): CommonResponse = CommonResponse.success(ScheduleResponseCode.EVENT_FIND_SUCCESS, getScheduleQueryService.findEvent(eventId)) + ): CommonResponse = + CommonResponse.success(ScheduleResponseCode.EVENT_FIND_SUCCESS, getScheduleQueryService.findEvent(eventId)) } From ffd59a9e9c873f6ce749a5aea0eddb4c3801b10a Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 18:46:34 +0900 Subject: [PATCH 41/62] =?UTF-8?q?refactor:=20Meeting=20=E2=86=92=20Session?= =?UTF-8?q?=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/query/GetAttendanceQueryService.kt | 2 +- .../domain/attendance/presentation/AttendanceAdminController.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt index 1120247b..0ec62b10 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt @@ -50,7 +50,7 @@ class GetAttendanceQueryService( return mapper.toDetailResponse(user, responses) } - fun findAllAttendanceByMeeting(sessionId: Long): List { + fun findAllAttendanceBySession(sessionId: Long): List { val session = sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException() } val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) return attendances.map(mapper::toInfoResponse) diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt index 5b033bce..86b82904 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceAdminController.kt @@ -44,7 +44,7 @@ class AttendanceAdminController( ): CommonResponse> = CommonResponse.success( AttendanceResponseCode.ATTENDANCE_FIND_DETAIL_SUCCESS, - getAttendanceQueryService.findAllAttendanceByMeeting(sessionId), + getAttendanceQueryService.findAllAttendanceBySession(sessionId), ) @PatchMapping("/status") From 9a903f6771a3e514b21c3357cd05f583e82f8706 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 19:05:44 +0900 Subject: [PATCH 42/62] =?UTF-8?q?style:=20GetAttendanceQueryService=20mapp?= =?UTF-8?q?er=20=ED=95=84=EB=93=9C=EB=AA=85=20attendanceMapper=EB=A1=9C=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/query/GetAttendanceQueryService.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt index 0ec62b10..86461ca3 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt @@ -22,7 +22,7 @@ class GetAttendanceQueryService( private val userCardinalGetService: UserCardinalGetService, private val sessionRepository: SessionRepository, private val attendanceRepository: AttendanceRepository, - private val mapper: AttendanceMapper, + private val attendanceMapper: AttendanceMapper, ) { fun findAttendance(userId: Long): AttendanceSummaryResponse { val user = userGetService.find(userId) @@ -35,7 +35,7 @@ class GetAttendanceQueryService( today.plusDays(1).atStartOfDay(), ) - return mapper.toSummaryResponse(user, todayAttendance, isAdmin = user.role == Role.ADMIN) + return attendanceMapper.toSummaryResponse(user, todayAttendance, isAdmin = user.role == Role.ADMIN) } fun findAllDetailsByCurrentCardinal(userId: Long): AttendanceDetailResponse { @@ -45,14 +45,14 @@ class GetAttendanceQueryService( val responses = attendanceRepository .findAllByUserIdAndCardinal(userId, currentCardinal.cardinalNumber) - .map(mapper::toResponse) + .map(attendanceMapper::toResponse) - return mapper.toDetailResponse(user, responses) + return attendanceMapper.toDetailResponse(user, responses) } fun findAllAttendanceBySession(sessionId: Long): List { val session = sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException() } val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) - return attendances.map(mapper::toInfoResponse) + return attendances.map(attendanceMapper::toInfoResponse) } } From a51974520bd6bdcf8e1569f846deda200c27bed6 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 19:10:11 +0900 Subject: [PATCH 43/62] =?UTF-8?q?docs:=20QR=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=9C=EC=84=9D=20=EA=B8=B0=EB=8A=A5=20=EC=98=88=EC=A0=95=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=EC=97=90=20TODO=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/attendance/domain/repository/AttendanceRepository.kt | 1 + .../weeth/domain/attendance/domain/repository/SessionReader.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt index ca914209..6c682427 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt @@ -77,6 +77,7 @@ interface AttendanceRepository : JpaRepository { @Param("cardinal") cardinal: Int, ): List + // TODO: QR 코드 출석 기능 구현 시 사용 예정 (여러 세션의 출석자 배치 조회) @Query("SELECT a FROM Attendance a JOIN FETCH a.user WHERE a.session IN :sessions") fun findAllBySessionIn( @Param("sessions") sessions: List, diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt index ad388199..e060a856 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt @@ -3,6 +3,7 @@ package com.weeth.domain.attendance.domain.repository import com.weeth.domain.attendance.domain.entity.Session import java.time.LocalDateTime +// TODO: QR 코드 출석 기능 구현 시 사용 예정 (현재 시간 기준 진행 중인 세션 조회) interface SessionReader { fun findAllByStartBetween( start: LocalDateTime, From 724f9c191a20d132ace802b962bfa3df11e8b8ee Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 19:14:55 +0900 Subject: [PATCH 44/62] =?UTF-8?q?refactor:=20schedule=20DTO=EC=97=90=20@Sc?= =?UTF-8?q?hema=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/request/ScheduleSaveRequest.kt | 7 +++++++ .../application/dto/request/ScheduleTimeRequest.kt | 3 +++ .../application/dto/request/ScheduleUpdateRequest.kt | 6 ++++++ .../schedule/application/dto/response/EventResponse.kt | 4 ++-- .../application/dto/response/SessionInfoResponse.kt | 2 +- .../application/dto/response/SessionInfosResponse.kt | 4 ++++ .../schedule/application/dto/response/SessionResponse.kt | 2 +- 7 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt index 8103ce5e..c9db649e 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt @@ -1,22 +1,29 @@ package com.weeth.domain.schedule.application.dto.request +import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotNull import org.springframework.format.annotation.DateTimeFormat import java.time.LocalDateTime data class ScheduleSaveRequest( + @field:Schema(description = "일정 제목", example = "MT") @field:NotBlank val title: String, + @field:Schema(description = "일정 내용", example = "1박 2일 MT입니다.") @field:NotBlank val content: String, + @field:Schema(description = "장소", example = "가평") @field:NotBlank val location: String, + @field:Schema(description = "기수", example = "4") @field:NotNull val cardinal: Int, + @field:Schema(description = "시작 시간", example = "2024-03-01T10:00:00") @field:NotNull @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) val start: LocalDateTime, + @field:Schema(description = "종료 시간", example = "2024-03-01T12:00:00") @field:NotNull @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) val end: LocalDateTime, diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleTimeRequest.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleTimeRequest.kt index 04b42642..debc0e90 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleTimeRequest.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleTimeRequest.kt @@ -1,13 +1,16 @@ package com.weeth.domain.schedule.application.dto.request +import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotNull import org.springframework.format.annotation.DateTimeFormat import java.time.LocalDateTime data class ScheduleTimeRequest( + @field:Schema(description = "시작 시간", example = "2024-03-01T10:00:00") @field:NotNull @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) val start: LocalDateTime, + @field:Schema(description = "종료 시간", example = "2024-03-01T12:00:00") @field:NotNull @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) val end: LocalDateTime, diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt index 60c9543d..f001d161 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt @@ -1,20 +1,26 @@ package com.weeth.domain.schedule.application.dto.request +import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotNull import org.springframework.format.annotation.DateTimeFormat import java.time.LocalDateTime data class ScheduleUpdateRequest( + @field:Schema(description = "일정 제목", example = "MT") @field:NotBlank val title: String, + @field:Schema(description = "일정 내용", example = "1박 2일 MT입니다.") @field:NotBlank val content: String, + @field:Schema(description = "장소", example = "가평") @field:NotBlank val location: String, + @field:Schema(description = "시작 시간", example = "2024-03-01T10:00:00") @field:NotNull @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) val start: LocalDateTime, + @field:Schema(description = "종료 시간", example = "2024-03-01T12:00:00") @field:NotNull @field:DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) val end: LocalDateTime, diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/EventResponse.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/EventResponse.kt index c3041d8a..f6d476a4 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/EventResponse.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/EventResponse.kt @@ -11,11 +11,11 @@ data class EventResponse( val title: String, @field:Schema(description = "일정 내용") val content: String, - @field:Schema(description = "장소", example = "공학관 401호") + @field:Schema(description = "장소", example = "가평") val location: String, @field:Schema(description = "작성자 이름", example = "이지훈") val name: String?, - @field:Schema(description = "기수", example = "8") + @field:Schema(description = "기수", example = "4") val cardinal: Int, @field:Schema(description = "일정 타입", example = "EVENT") val type: Type, diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfoResponse.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfoResponse.kt index 3b5eb2cd..2a93789e 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfoResponse.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfoResponse.kt @@ -6,7 +6,7 @@ import java.time.LocalDateTime data class SessionInfoResponse( @field:Schema(description = "정기모임 ID", example = "1") val id: Long, - @field:Schema(description = "기수", example = "8") + @field:Schema(description = "기수", example = "4") val cardinal: Int, @field:Schema(description = "제목", example = "1차 정기모임") val title: String, diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt index 5fd6c007..edafcc97 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt @@ -1,6 +1,10 @@ package com.weeth.domain.schedule.application.dto.response +import io.swagger.v3.oas.annotations.media.Schema + data class SessionInfosResponse( + @field:Schema(description = "이번 주 정기모임") val thisWeek: SessionInfoResponse?, + @field:Schema(description = "정기모임 목록") val meetings: List, ) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionResponse.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionResponse.kt index a94105b4..7c06371f 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionResponse.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionResponse.kt @@ -17,7 +17,7 @@ data class SessionResponse( val location: String?, @field:Schema(description = "작성자 이름", example = "이지훈") val name: String?, - @field:Schema(description = "기수", example = "8") + @field:Schema(description = "기수", example = "4") val cardinal: Int, @field:Schema(description = "일정 타입", example = "MEETING") val type: Type, From 9ef68f272c6bda424e0af43df8459c155983fc93 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 19:36:16 +0900 Subject: [PATCH 45/62] =?UTF-8?q?refactor:=20Meeting=20=E2=86=92=20Session?= =?UTF-8?q?=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/query/GetAttendanceQueryServiceTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt index dec774aa..553876ae 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt @@ -120,7 +120,7 @@ class GetAttendanceQueryServiceTest : every { attendanceMapper.toInfoResponse(attendance1) } returns response1 every { attendanceMapper.toInfoResponse(attendance2) } returns response2 - val result = queryService.findAllAttendanceByMeeting(sessionId) + val result = queryService.findAllAttendanceBySession(sessionId) result shouldBe listOf(response1, response2) } From fd3fa97193a081bdad68b9aa765fd31fd8b35be2 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 20:10:35 +0900 Subject: [PATCH 46/62] =?UTF-8?q?fix:=20=EB=A8=B8=EC=A7=80=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20UserManageUseCaseImpl?= =?UTF-8?q?=20=EC=BB=B4=ED=8C=8C=EC=9D=BC=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/application/usecase/UserManageUseCaseImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java b/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java index fc00d1e8..655b6f6b 100644 --- a/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java +++ b/src/main/java/com/weeth/domain/user/application/usecase/UserManageUseCaseImpl.java @@ -34,8 +34,8 @@ public class UserManageUseCaseImpl implements UserManageUseCase { private final UserUpdateService userUpdateService; private final UserDeleteService userDeleteService; - private final AttendanceSaveService attendanceSaveService; - private final MeetingGetService meetingGetService; + private final AttendanceRepository attendanceRepository; + private final SessionRepository sessionRepository; private final RefreshTokenStorePort refreshTokenStorePort; private final CardinalGetService cardinalGetService; private final UserCardinalSaveService userCardinalSaveService; From d3f024919ed4e158f59b00c8f3306b74eca631d2 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 20:16:17 +0900 Subject: [PATCH 47/62] =?UTF-8?q?fix:=20=EC=B6=9C=EC=84=9D=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EA=B4=80=EB=A6=AC=EC=9E=90=20=EC=9E=AC=EC=A0=95=20?= =?UTF-8?q?=EC=8B=9C=20=EC=83=81=ED=83=9C=20=EA=B2=80=EC=A6=9D=20=EB=B0=8F?= =?UTF-8?q?=20=EC=B9=B4=EC=9A=B4=ED=84=B0=20=EB=B3=B4=EC=A0=95=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/command/ManageAttendanceUseCase.kt | 11 +++++++---- .../domain/attendance/domain/entity/Attendance.kt | 4 ++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt index 0cc25838..7accfe02 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt @@ -76,13 +76,16 @@ class ManageAttendanceUseCase( ?: throw AttendanceNotFoundException() val user = attendance.user val newStatus = AttendanceStatus.valueOf(update.status) + + if (attendance.status == newStatus) return@forEach + + val prevStatus = attendance.status + attendance.adminOverride(newStatus) if (newStatus == AttendanceStatus.ABSENT) { - attendance.close() - user.removeAttend() + if (prevStatus == AttendanceStatus.ATTEND) user.removeAttend() user.absent() } else { - attendance.attend() - user.removeAbsent() + if (prevStatus == AttendanceStatus.ABSENT) user.removeAbsent() user.attend() } } diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt index 7b31cb87..7d58e2e1 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt @@ -46,6 +46,10 @@ class Attendance( // 기존 close() 는 absent() 로 대체 (AttendanceUpdateService 호환 유지) fun close() = absent() + fun adminOverride(newStatus: AttendanceStatus) { + status = newStatus + } + fun isPending(): Boolean = status == AttendanceStatus.PENDING fun isWrong(code: Int): Boolean = !session.isCodeMatch(code) From 23c09c8a698d29c84dad883350ef093c2c9b242e Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 20:19:19 +0900 Subject: [PATCH 48/62] =?UTF-8?q?test:=20UserManageUseCaseTest=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=EC=9D=84=20=EC=8B=A4=EC=A0=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=B2=B4=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/application/usecase/UserManageUseCaseTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt b/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt index fdb62dde..13d925ed 100644 --- a/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt +++ b/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt @@ -38,8 +38,8 @@ class UserManageUseCaseTest : val userGetService = mockk() val userUpdateService = mockk(relaxUnitFun = true) val userDeleteService = mockk(relaxUnitFun = true) - val attendanceSaveService = mockk(relaxUnitFun = true) - val meetingGetService = mockk() + val attendanceRepository = mockk(relaxUnitFun = true) + val sessionRepository = mockk() val refreshTokenStorePort = mockk(relaxUnitFun = true) val cardinalGetService = mockk() val userCardinalSaveService = mockk(relaxUnitFun = true) @@ -52,8 +52,8 @@ class UserManageUseCaseTest : userGetService, userUpdateService, userDeleteService, - attendanceSaveService, - meetingGetService, + attendanceRepository, + sessionRepository, refreshTokenStorePort, cardinalGetService, userCardinalSaveService, From 11bdf5ac9067bb2f577ad4caeb98f4bc0f082714 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 20:22:27 +0900 Subject: [PATCH 49/62] =?UTF-8?q?fix:=20SessionNotFoundException=EC=9D=84?= =?UTF-8?q?=20attendance=20=EB=8F=84=EB=A9=94=EC=9D=B8=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../attendance/application/exception/AttendanceErrorCode.kt | 3 +++ .../application/exception/SessionNotFoundException.kt | 5 +++++ .../application/usecase/query/GetAttendanceQueryService.kt | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionNotFoundException.kt diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/exception/AttendanceErrorCode.kt b/src/main/kotlin/com/weeth/domain/attendance/application/exception/AttendanceErrorCode.kt index bba918e5..426f447a 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/exception/AttendanceErrorCode.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/exception/AttendanceErrorCode.kt @@ -17,6 +17,9 @@ enum class AttendanceErrorCode( @ExplainError("사용자가 출석 일정을 직접 수정하려고 시도할 때 발생합니다. (출석 로직 위반)") ATTENDANCE_EVENT_TYPE_NOT_MATCH(2202, HttpStatus.BAD_REQUEST, "출석일정은 직접 수정할 수 없습니다."), + + @ExplainError("세션 ID로 조회했으나 해당 세션이 존재하지 않을 때 발생합니다.") + SESSION_NOT_FOUND(2203, HttpStatus.NOT_FOUND, "존재하지 않는 세션입니다."), ; override fun getCode(): Int = code diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionNotFoundException.kt b/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionNotFoundException.kt new file mode 100644 index 00000000..c8cf32d2 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionNotFoundException.kt @@ -0,0 +1,5 @@ +package com.weeth.domain.attendance.application.exception + +import com.weeth.global.common.exception.BaseException + +class SessionNotFoundException : BaseException(AttendanceErrorCode.SESSION_NOT_FOUND) diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt index 86461ca3..494c0963 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt @@ -6,7 +6,7 @@ import com.weeth.domain.attendance.application.dto.response.AttendanceSummaryRes import com.weeth.domain.attendance.application.mapper.AttendanceMapper import com.weeth.domain.attendance.domain.repository.AttendanceRepository import com.weeth.domain.attendance.domain.repository.SessionRepository -import com.weeth.domain.schedule.application.exception.MeetingNotFoundException +import com.weeth.domain.attendance.application.exception.SessionNotFoundException import com.weeth.domain.user.domain.entity.enums.Role import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.service.UserCardinalGetService @@ -51,7 +51,7 @@ class GetAttendanceQueryService( } fun findAllAttendanceBySession(sessionId: Long): List { - val session = sessionRepository.findById(sessionId).orElseThrow { MeetingNotFoundException() } + val session = sessionRepository.findById(sessionId).orElseThrow { SessionNotFoundException() } val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) return attendances.map(attendanceMapper::toInfoResponse) } From ae4a58faec638cbc25e0e765a2f1cd7dae222c39 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 20:29:06 +0900 Subject: [PATCH 50/62] =?UTF-8?q?refactor:=20findSessionInfos=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=A0=95=EB=A0=AC=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/query/GetSessionQueryService.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt index 0d58a315..9a83cf0f 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt @@ -43,8 +43,7 @@ class GetSessionQueryService( sessionRepository.findAllByCardinalOrderByStartDesc(cardinal) } val thisWeek = findThisWeek(sessions) - val sorted = sessions.sortedByDescending { it.start } - return sessionMapper.toInfos(thisWeek, sorted) + return sessionMapper.toInfos(thisWeek, sessions) } private fun findThisWeek(sessions: List): Session? { From 387a464c6406f1e5b77cdc9c386cc1e2a01783df Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 20:29:37 +0900 Subject: [PATCH 51/62] =?UTF-8?q?refactor:=20Meeting=20=E2=86=92=20Session?= =?UTF-8?q?=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/query/GetAttendanceQueryServiceTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt index 553876ae..79884394 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt @@ -104,7 +104,7 @@ class GetAttendanceQueryServiceTest : } } - describe("findAllAttendanceByMeeting") { + describe("findAllAttendanceBySession") { it("해당 정기모임의 출석 정보를 조회") { val sessionId = 1L val session = mockk() From d11ef69113984f2e4ac80bd183fc16b55beca288 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 20:31:02 +0900 Subject: [PATCH 52/62] =?UTF-8?q?fix:=20Session.updateInfo=EC=97=90=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/weeth/domain/attendance/domain/entity/Session.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt index af39a8e8..a552fdb6 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt @@ -50,6 +50,8 @@ class Session( end: LocalDateTime, user: User?, ) { + require(title.isNotBlank()) { "제목은 필수입니다" } + require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" } this.title = title this.content = content this.location = location From afe7f2182e200ac13348e2175d556159bef21b17 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 20:41:04 +0900 Subject: [PATCH 53/62] =?UTF-8?q?fix:=20Event=20update=EC=97=90=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/weeth/domain/schedule/domain/entity/Event.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt index 741585ca..0efd8791 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt @@ -37,6 +37,8 @@ class Event( end: LocalDateTime, user: User?, ) { + require(title.isNotBlank()) { "제목은 필수입니다" } + require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" } this.title = title this.content = content this.location = location From 5fba850d475621a7ad52206830c021f45cbc8691 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 20:46:18 +0900 Subject: [PATCH 54/62] =?UTF-8?q?refactor:=20ScheduleController=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20@ApiErrorCod?= =?UTF-8?q?eExample=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/query/GetAttendanceQueryService.kt | 2 +- .../weeth/domain/schedule/presentation/ScheduleController.kt | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt index 494c0963..83cb7b8c 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt @@ -3,10 +3,10 @@ package com.weeth.domain.attendance.application.usecase.query import com.weeth.domain.attendance.application.dto.response.AttendanceDetailResponse import com.weeth.domain.attendance.application.dto.response.AttendanceInfoResponse import com.weeth.domain.attendance.application.dto.response.AttendanceSummaryResponse +import com.weeth.domain.attendance.application.exception.SessionNotFoundException import com.weeth.domain.attendance.application.mapper.AttendanceMapper import com.weeth.domain.attendance.domain.repository.AttendanceRepository import com.weeth.domain.attendance.domain.repository.SessionRepository -import com.weeth.domain.attendance.application.exception.SessionNotFoundException import com.weeth.domain.user.domain.entity.enums.Role import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.service.UserCardinalGetService diff --git a/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleController.kt b/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleController.kt index e7999833..3e2224b6 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleController.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleController.kt @@ -1,10 +1,7 @@ package com.weeth.domain.schedule.presentation import com.weeth.domain.schedule.application.dto.response.ScheduleResponse -import com.weeth.domain.schedule.application.exception.EventErrorCode -import com.weeth.domain.schedule.application.exception.MeetingErrorCode import com.weeth.domain.schedule.application.usecase.query.GetScheduleQueryService -import com.weeth.global.common.exception.ApiErrorCodeExample import com.weeth.global.common.response.CommonResponse import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag @@ -18,7 +15,6 @@ import java.time.LocalDateTime @Tag(name = "SCHEDULE", description = "캘린더 조회 API") @RestController @RequestMapping("/api/v4/schedules") -@ApiErrorCodeExample(EventErrorCode::class, MeetingErrorCode::class) class ScheduleController( private val getScheduleQueryService: GetScheduleQueryService, ) { From cf968fb959c0c2874f5496af0c6bdcc2d4887794 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 20 Feb 2026 21:07:20 +0900 Subject: [PATCH 55/62] =?UTF-8?q?refactor:=20AttendanceRepository=20mock?= =?UTF-8?q?=EC=9D=84=20relaxed=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98?= =?UTF-8?q?=EC=97=AC=20saveAll=20=ED=98=B8=EC=B6=9C=20=EC=98=A4=EB=A5=98?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/application/usecase/UserManageUseCaseTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt b/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt index 13d925ed..080855d0 100644 --- a/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt +++ b/src/test/kotlin/com/weeth/domain/user/application/usecase/UserManageUseCaseTest.kt @@ -38,7 +38,7 @@ class UserManageUseCaseTest : val userGetService = mockk() val userUpdateService = mockk(relaxUnitFun = true) val userDeleteService = mockk(relaxUnitFun = true) - val attendanceRepository = mockk(relaxUnitFun = true) + val attendanceRepository = mockk(relaxed = true) val sessionRepository = mockk() val refreshTokenStorePort = mockk(relaxUnitFun = true) val cardinalGetService = mockk() From 705d12456371701864d9248486183dad49d77b27 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 24 Feb 2026 16:33:52 +0900 Subject: [PATCH 56/62] =?UTF-8?q?refactor:=20Meeting=20->=20Session=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/ManageAttendanceUseCase.kt | 4 +- .../usecase/command/ManageSessionUseCase.kt | 7 ++- .../usecase/query/GetSessionQueryService.kt | 4 +- .../presentation/AttendanceResponseCode.kt | 2 +- .../presentation/SessionAdminController.kt | 6 +-- .../presentation/SessionController.kt | 4 +- .../dto/response/ScheduleResponse.kt | 2 +- .../dto/response/SessionInfosResponse.kt | 2 +- ...eetingErrorCode.kt => SessionErrorCode.kt} | 4 +- ...ception.kt => SessionNotFoundException.kt} | 2 +- .../application/mapper/ScheduleMapper.kt | 8 ++-- .../application/mapper/SessionMapper.kt | 6 +-- .../schedule/domain/entity/enums/Type.kt | 2 +- .../controller/ExceptionDocController.kt | 4 +- .../mapper/AttendanceMapperTest.kt | 46 +++++++++---------- 15 files changed, 51 insertions(+), 52 deletions(-) rename src/main/kotlin/com/weeth/domain/schedule/application/exception/{MeetingErrorCode.kt => SessionErrorCode.kt} (87%) rename src/main/kotlin/com/weeth/domain/schedule/application/exception/{MeetingNotFoundException.kt => SessionNotFoundException.kt} (57%) diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt index 7accfe02..7b601509 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt @@ -8,7 +8,7 @@ import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus import com.weeth.domain.attendance.domain.entity.enums.SessionStatus import com.weeth.domain.attendance.domain.repository.AttendanceRepository import com.weeth.domain.attendance.domain.repository.SessionRepository -import com.weeth.domain.schedule.application.exception.MeetingNotFoundException +import com.weeth.domain.schedule.application.exception.SessionNotFoundException import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.service.UserGetService import org.springframework.stereotype.Service @@ -53,7 +53,7 @@ class ManageAttendanceUseCase( sessionRepository .findAllByCardinalOrderByStartAsc(cardinal) .firstOrNull { session -> session.start.toLocalDate().isEqual(now) && session.end.toLocalDate().isEqual(now) } - ?: throw MeetingNotFoundException() + ?: throw SessionNotFoundException() val attendances = attendanceRepository.findAllBySessionAndUserStatus(targetSession, Status.ACTIVE) closePendingAttendances(attendances) } diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt index ee1b4f28..c05262a7 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt @@ -6,12 +6,11 @@ import com.weeth.domain.attendance.domain.repository.AttendanceRepository import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest -import com.weeth.domain.schedule.application.exception.MeetingNotFoundException +import com.weeth.domain.schedule.application.exception.SessionNotFoundException import com.weeth.domain.schedule.application.mapper.SessionMapper import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.service.CardinalGetService import com.weeth.domain.user.domain.service.UserGetService -import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -42,14 +41,14 @@ class ManageSessionUseCase( request: ScheduleUpdateRequest, userId: Long, ) { - val session = sessionRepository.findByIdWithLock(sessionId) ?: throw MeetingNotFoundException() + val session = sessionRepository.findByIdWithLock(sessionId) ?: throw SessionNotFoundException() val user = userGetService.find(userId) session.updateInfo(request.title, request.content, request.location, request.start, request.end, user) } @Transactional fun delete(sessionId: Long) { - val session = sessionRepository.findByIdWithLock(sessionId) ?: throw MeetingNotFoundException() + val session = sessionRepository.findByIdWithLock(sessionId) ?: throw SessionNotFoundException() val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) attendances.forEach { a -> when (a.status) { diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt index 9a83cf0f..ee5a61ab 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt @@ -4,7 +4,7 @@ import com.weeth.domain.attendance.domain.entity.Session import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse import com.weeth.domain.schedule.application.dto.response.SessionResponse -import com.weeth.domain.schedule.application.exception.MeetingNotFoundException +import com.weeth.domain.schedule.application.exception.SessionNotFoundException import com.weeth.domain.schedule.application.mapper.SessionMapper import com.weeth.domain.user.domain.entity.enums.Role import com.weeth.domain.user.domain.service.UserGetService @@ -27,7 +27,7 @@ class GetSessionQueryService( sessionId: Long, ): SessionResponse { val user = userGetService.find(userId) - val session = sessionRepository.findByIdOrNull(sessionId) ?: throw MeetingNotFoundException() + val session = sessionRepository.findByIdOrNull(sessionId) ?: throw SessionNotFoundException() return if (user.role == Role.ADMIN) { sessionMapper.toAdminResponse(session) } else { diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt index 7d68fb7e..651bd524 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt @@ -12,7 +12,7 @@ enum class AttendanceResponseCode( ATTENDANCE_CLOSE_SUCCESS(1200, HttpStatus.OK, "출석이 성공적으로 마감되었습니다."), ATTENDANCE_UPDATED_SUCCESS(1201, HttpStatus.OK, "개별 출석 상태가 성공적으로 수정되었습니다."), ATTENDANCE_FIND_DETAIL_SUCCESS(1202, HttpStatus.OK, "모든 인원의 정기모임 출석 정보가 성공적으로 조회되었습니다."), - MEETING_FIND_SUCCESS(1203, HttpStatus.OK, "기수별 정기모임 리스트를 성공적으로 조회했습니다."), + SESSION_INFOS_FIND_SUCCESS(1203, HttpStatus.OK, "기수별 정기모임 리스트를 성공적으로 조회했습니다."), // AttendanceController 관련 ATTENDANCE_CHECKIN_SUCCESS(1204, HttpStatus.OK, "출석이 성공적으로 처리되었습니다."), diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt index 21959a91..0c59e875 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt @@ -5,7 +5,7 @@ import com.weeth.domain.attendance.application.usecase.query.GetSessionQueryServ import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse -import com.weeth.domain.schedule.application.exception.MeetingErrorCode +import com.weeth.domain.schedule.application.exception.SessionErrorCode import com.weeth.global.auth.annotation.CurrentUser import com.weeth.global.common.exception.ApiErrorCodeExample import com.weeth.global.common.response.CommonResponse @@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.RestController @Tag(name = "SESSION ADMIN", description = "[ADMIN] 정기모임 어드민 API") @RestController @RequestMapping("/api/v4/admin/sessions") -@ApiErrorCodeExample(MeetingErrorCode::class) +@ApiErrorCodeExample(SessionErrorCode::class) class SessionAdminController( private val manageSessionUseCase: ManageSessionUseCase, private val getSessionQueryService: GetSessionQueryService, @@ -66,5 +66,5 @@ class SessionAdminController( fun getSessionInfos( @RequestParam(required = false) cardinal: Int?, ): CommonResponse = - CommonResponse.success(AttendanceResponseCode.MEETING_FIND_SUCCESS, getSessionQueryService.findSessionInfos(cardinal)) + CommonResponse.success(AttendanceResponseCode.SESSION_INFOS_FIND_SUCCESS, getSessionQueryService.findSessionInfos(cardinal)) } diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt index 98f60beb..b7c3b9d6 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt @@ -2,7 +2,7 @@ package com.weeth.domain.attendance.presentation import com.weeth.domain.attendance.application.usecase.query.GetSessionQueryService import com.weeth.domain.schedule.application.dto.response.SessionResponse -import com.weeth.domain.schedule.application.exception.MeetingErrorCode +import com.weeth.domain.schedule.application.exception.SessionErrorCode import com.weeth.global.auth.annotation.CurrentUser import com.weeth.global.common.exception.ApiErrorCodeExample import com.weeth.global.common.response.CommonResponse @@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.RestController @Tag(name = "SESSION", description = "정기모임 API") @RestController @RequestMapping("/api/v4/sessions") -@ApiErrorCodeExample(MeetingErrorCode::class) +@ApiErrorCodeExample(SessionErrorCode::class) class SessionController( private val getSessionQueryService: GetSessionQueryService, ) { diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/ScheduleResponse.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/ScheduleResponse.kt index 80a05f33..1a0d883f 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/ScheduleResponse.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/ScheduleResponse.kt @@ -13,5 +13,5 @@ data class ScheduleResponse( @field:Schema(description = "종료 시간") val end: LocalDateTime, @field:Schema(description = "정기모임 여부") - val isMeeting: Boolean, + val isSession: Boolean, ) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt index edafcc97..88409aa5 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/response/SessionInfosResponse.kt @@ -6,5 +6,5 @@ data class SessionInfosResponse( @field:Schema(description = "이번 주 정기모임") val thisWeek: SessionInfoResponse?, @field:Schema(description = "정기모임 목록") - val meetings: List, + val sessions: List, ) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingErrorCode.kt b/src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionErrorCode.kt similarity index 87% rename from src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingErrorCode.kt rename to src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionErrorCode.kt index 21427fd7..d1db7469 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingErrorCode.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionErrorCode.kt @@ -4,13 +4,13 @@ import com.weeth.global.common.exception.ErrorCodeInterface import com.weeth.global.common.exception.ExplainError import org.springframework.http.HttpStatus -enum class MeetingErrorCode( +enum class SessionErrorCode( private val code: Int, private val status: HttpStatus, private val message: String, ) : ErrorCodeInterface { @ExplainError("요청한 정기모임 ID에 해당하는 정기모임이 존재하지 않을 때 발생합니다.") - MEETING_NOT_FOUND(2701, HttpStatus.NOT_FOUND, "존재하지 않는 정기모임입니다."), + SESSION_NOT_FOUND(2701, HttpStatus.NOT_FOUND, "존재하지 않는 정기모임입니다."), ; override fun getCode(): Int = code diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingNotFoundException.kt b/src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionNotFoundException.kt similarity index 57% rename from src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingNotFoundException.kt rename to src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionNotFoundException.kt index ab3ace42..0d545603 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/exception/MeetingNotFoundException.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionNotFoundException.kt @@ -2,4 +2,4 @@ package com.weeth.domain.schedule.application.exception import com.weeth.global.common.exception.BaseException -class MeetingNotFoundException : BaseException(MeetingErrorCode.MEETING_NOT_FOUND) +class SessionNotFoundException : BaseException(SessionErrorCode.SESSION_NOT_FOUND) diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt index b86d1774..628ab3d8 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt @@ -9,25 +9,25 @@ import org.springframework.stereotype.Component class ScheduleMapper { fun toResponse( event: Event, - isMeeting: Boolean, + isSession: Boolean, ): ScheduleResponse = ScheduleResponse( id = event.id, title = event.title, start = event.start, end = event.end, - isMeeting = isMeeting, + isSession = isSession, ) fun toResponse( session: Session, - isMeeting: Boolean, + isSession: Boolean, ): ScheduleResponse = ScheduleResponse( id = session.id, title = session.title, start = session.start, end = session.end, - isMeeting = isMeeting, + isSession = isSession, ) } diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt index 96b5bbf8..0a8ff978 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt @@ -19,7 +19,7 @@ class SessionMapper { location = session.location, name = session.user?.name, cardinal = session.cardinal, - type = Type.MEETING, + type = Type.SESSION, code = null, start = session.start, end = session.end, @@ -35,7 +35,7 @@ class SessionMapper { location = session.location, name = session.user?.name, cardinal = session.cardinal, - type = Type.MEETING, + type = Type.SESSION, code = session.code, start = session.start, end = session.end, @@ -57,7 +57,7 @@ class SessionMapper { ): SessionInfosResponse = SessionInfosResponse( thisWeek = thisWeek?.let { toInfo(it) }, - meetings = sessions.map { toInfo(it) }, + sessions = sessions.map { toInfo(it) }, ) fun toEntity( diff --git a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/enums/Type.kt b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/enums/Type.kt index 367e6a0b..bbb663b4 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/enums/Type.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/enums/Type.kt @@ -2,5 +2,5 @@ package com.weeth.domain.schedule.domain.entity.enums enum class Type { EVENT, - MEETING, + SESSION, } diff --git a/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt b/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt index 1d67f2c0..443733ee 100644 --- a/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt +++ b/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt @@ -6,7 +6,7 @@ import com.weeth.domain.board.application.exception.BoardErrorCode import com.weeth.domain.comment.application.exception.CommentErrorCode import com.weeth.domain.penalty.application.exception.PenaltyErrorCode import com.weeth.domain.schedule.application.exception.EventErrorCode -import com.weeth.domain.schedule.application.exception.MeetingErrorCode +import com.weeth.domain.schedule.application.exception.SessionErrorCode import com.weeth.domain.user.application.exception.UserErrorCode import com.weeth.global.auth.jwt.application.exception.JwtErrorCode import com.weeth.global.common.exception.ApiErrorCodeExample @@ -46,7 +46,7 @@ class ExceptionDocController { @GetMapping("/schedule") @Operation(summary = "Schedule 도메인 에러 코드 목록") - @ApiErrorCodeExample(EventErrorCode::class, MeetingErrorCode::class) + @ApiErrorCodeExample(EventErrorCode::class, SessionErrorCode::class) fun scheduleErrorCodes() { } diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt index 3b814285..461bc719 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt @@ -22,18 +22,18 @@ class AttendanceMapperTest : describe("toSummaryResponse") { it("사용자 + 당일 출석 객체를 MainResponse로 매핑한다") { val today = LocalDate.now() - val meeting = createOneDaySession(today, 1, 1111, "Today") + val session = createOneDaySession(today, 1, 1111, "Today") val user = createActiveUser("이지훈") - val attendance = createAttendance(meeting, user) + val attendance = createAttendance(session, user) val main = mapper.toSummaryResponse(user, attendance) main.shouldNotBeNull() - main.title shouldBe meeting.title + main.title shouldBe session.title main.status shouldBe attendance.status - main.start shouldBe meeting.start - main.end shouldBe meeting.end - main.location shouldBe meeting.location + main.start shouldBe session.start + main.end shouldBe session.end + main.location shouldBe session.location } it("attendance가 null이면 필드는 null로 매핑") { @@ -50,49 +50,49 @@ class AttendanceMapperTest : it("일반 유저는 출석 코드가 null로 매핑된다") { val today = LocalDate.now() - val meeting = createOneDaySession(today, 1, 1234, "Today") + val session = createOneDaySession(today, 1, 1234, "Today") val user = createActiveUser("일반유저") - val attendance = createAttendance(meeting, user) + val attendance = createAttendance(session, user) val main = mapper.toSummaryResponse(user, attendance) main.shouldNotBeNull() main.code.shouldBeNull() - main.title shouldBe meeting.title + main.title shouldBe session.title main.status shouldBe attendance.status } it("ADMIN 유저는 출석 코드가 포함된다") { val today = LocalDate.now() val expectedCode = 1234 - val meeting = createOneDaySession(today, 1, expectedCode, "Today") + val session = createOneDaySession(today, 1, expectedCode, "Today") val adminUser = createAdminUser("관리자") - val attendance = createAttendance(meeting, adminUser) + val attendance = createAttendance(session, adminUser) val main = mapper.toSummaryResponse(adminUser, attendance, isAdmin = true) main.shouldNotBeNull() main.code shouldBe expectedCode - main.title shouldBe meeting.title - main.start shouldBe meeting.start - main.end shouldBe meeting.end - main.location shouldBe meeting.location + main.title shouldBe session.title + main.start shouldBe session.start + main.end shouldBe session.end + main.location shouldBe session.location } } describe("toResponse") { it("단일 출석을 AttendanceResponse로 매핑한다") { - val meeting = createOneDaySession(LocalDate.now().minusDays(1), 1, 2222, "D-1") + val session = createOneDaySession(LocalDate.now().minusDays(1), 1, 2222, "D-1") val user = createActiveUser("사용자A") - val attendance = createAttendance(meeting, user) + val attendance = createAttendance(session, user) val response = mapper.toResponse(attendance) response.shouldNotBeNull() - response.title shouldBe meeting.title - response.start shouldBe meeting.start - response.end shouldBe meeting.end - response.location shouldBe meeting.location + response.title shouldBe session.title + response.start shouldBe session.start + response.end shouldBe session.end + response.location shouldBe session.location } } @@ -120,11 +120,11 @@ class AttendanceMapperTest : describe("toInfoResponse") { it("Attendance를 InfoResponse로 매핑") { - val meeting = createOneDaySession(LocalDate.now(), 1, 3333, "Info") + val session = createOneDaySession(LocalDate.now(), 1, 3333, "Info") val user = createActiveUser("유저B") enrichUserProfile(user, Position.BE, "컴퓨터공학과", "20201234") - val attendance = createAttendance(meeting, user) + val attendance = createAttendance(session, user) setAttendanceId(attendance, 10L) val info = mapper.toInfoResponse(attendance) From 577cfde4b3536b8a04470681ea75288215a6ae75 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 24 Feb 2026 16:38:00 +0900 Subject: [PATCH 57/62] =?UTF-8?q?refactor:=20ResponseCode=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=B2=88=ED=98=B8=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/schedule/presentation/ScheduleResponseCode.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleResponseCode.kt b/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleResponseCode.kt index f0efd119..92230c61 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleResponseCode.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/presentation/ScheduleResponseCode.kt @@ -12,6 +12,6 @@ enum class ScheduleResponseCode( EVENT_UPDATE_SUCCESS(1701, HttpStatus.OK, "일정이 성공적으로 수정되었습니다."), EVENT_DELETE_SUCCESS(1702, HttpStatus.OK, "일정이 성공적으로 삭제되었습니다."), EVENT_FIND_SUCCESS(1703, HttpStatus.OK, "일정이 성공적으로 조회되었습니다."), - SCHEDULE_MONTHLY_FIND_SUCCESS(1710, HttpStatus.OK, "월별 일정이 성공적으로 조회되었습니다."), - SCHEDULE_YEARLY_FIND_SUCCESS(1711, HttpStatus.OK, "연도별 일정이 성공적으로 조회되었습니다."), + SCHEDULE_MONTHLY_FIND_SUCCESS(1704, HttpStatus.OK, "월별 일정이 성공적으로 조회되었습니다."), + SCHEDULE_YEARLY_FIND_SUCCESS(1705, HttpStatus.OK, "연도별 일정이 성공적으로 조회되었습니다."), } From a0e7a82012381a0698069a6133472b469a635c2f Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 24 Feb 2026 16:54:03 +0900 Subject: [PATCH 58/62] =?UTF-8?q?refactor:=20SessionErrorCode,=20SessionNo?= =?UTF-8?q?tFoundException=EB=A5=BC=20attendance=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../attendance/application/exception/AttendanceErrorCode.kt | 3 --- .../application/exception/SessionErrorCode.kt | 4 ++-- .../application/exception/SessionNotFoundException.kt | 2 +- .../application/usecase/command/ManageAttendanceUseCase.kt | 2 +- .../application/usecase/command/ManageSessionUseCase.kt | 2 +- .../application/usecase/query/GetSessionQueryService.kt | 2 +- .../attendance/presentation/SessionAdminController.kt | 2 +- .../domain/attendance/presentation/SessionController.kt | 2 +- .../application/exception/SessionNotFoundException.kt | 5 ----- .../global/common/controller/ExceptionDocController.kt | 6 +++--- 10 files changed, 11 insertions(+), 19 deletions(-) rename src/main/kotlin/com/weeth/domain/{schedule => attendance}/application/exception/SessionErrorCode.kt (83%) delete mode 100644 src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionNotFoundException.kt diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/exception/AttendanceErrorCode.kt b/src/main/kotlin/com/weeth/domain/attendance/application/exception/AttendanceErrorCode.kt index 426f447a..bba918e5 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/exception/AttendanceErrorCode.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/exception/AttendanceErrorCode.kt @@ -17,9 +17,6 @@ enum class AttendanceErrorCode( @ExplainError("사용자가 출석 일정을 직접 수정하려고 시도할 때 발생합니다. (출석 로직 위반)") ATTENDANCE_EVENT_TYPE_NOT_MATCH(2202, HttpStatus.BAD_REQUEST, "출석일정은 직접 수정할 수 없습니다."), - - @ExplainError("세션 ID로 조회했으나 해당 세션이 존재하지 않을 때 발생합니다.") - SESSION_NOT_FOUND(2203, HttpStatus.NOT_FOUND, "존재하지 않는 세션입니다."), ; override fun getCode(): Int = code diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionErrorCode.kt b/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionErrorCode.kt similarity index 83% rename from src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionErrorCode.kt rename to src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionErrorCode.kt index d1db7469..5b746258 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionErrorCode.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionErrorCode.kt @@ -1,4 +1,4 @@ -package com.weeth.domain.schedule.application.exception +package com.weeth.domain.attendance.application.exception import com.weeth.global.common.exception.ErrorCodeInterface import com.weeth.global.common.exception.ExplainError @@ -10,7 +10,7 @@ enum class SessionErrorCode( private val message: String, ) : ErrorCodeInterface { @ExplainError("요청한 정기모임 ID에 해당하는 정기모임이 존재하지 않을 때 발생합니다.") - SESSION_NOT_FOUND(2701, HttpStatus.NOT_FOUND, "존재하지 않는 정기모임입니다."), + SESSION_NOT_FOUND(2203, HttpStatus.NOT_FOUND, "존재하지 않는 정기모임입니다."), ; override fun getCode(): Int = code diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionNotFoundException.kt b/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionNotFoundException.kt index c8cf32d2..027e0bf7 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionNotFoundException.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionNotFoundException.kt @@ -2,4 +2,4 @@ package com.weeth.domain.attendance.application.exception import com.weeth.global.common.exception.BaseException -class SessionNotFoundException : BaseException(AttendanceErrorCode.SESSION_NOT_FOUND) +class SessionNotFoundException : BaseException(SessionErrorCode.SESSION_NOT_FOUND) diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt index 7b601509..e9109a4e 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt @@ -3,12 +3,12 @@ package com.weeth.domain.attendance.application.usecase.command import com.weeth.domain.attendance.application.dto.request.UpdateAttendanceStatusRequest import com.weeth.domain.attendance.application.exception.AttendanceCodeMismatchException import com.weeth.domain.attendance.application.exception.AttendanceNotFoundException +import com.weeth.domain.attendance.application.exception.SessionNotFoundException import com.weeth.domain.attendance.domain.entity.Attendance import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus import com.weeth.domain.attendance.domain.entity.enums.SessionStatus import com.weeth.domain.attendance.domain.repository.AttendanceRepository import com.weeth.domain.attendance.domain.repository.SessionRepository -import com.weeth.domain.schedule.application.exception.SessionNotFoundException import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.service.UserGetService import org.springframework.stereotype.Service diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt index c05262a7..b035f0af 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt @@ -1,12 +1,12 @@ package com.weeth.domain.attendance.application.usecase.command +import com.weeth.domain.attendance.application.exception.SessionNotFoundException import com.weeth.domain.attendance.domain.entity.Attendance import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus import com.weeth.domain.attendance.domain.repository.AttendanceRepository import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest -import com.weeth.domain.schedule.application.exception.SessionNotFoundException import com.weeth.domain.schedule.application.mapper.SessionMapper import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.service.CardinalGetService diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt index ee5a61ab..9fbf4098 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt @@ -1,10 +1,10 @@ package com.weeth.domain.attendance.application.usecase.query +import com.weeth.domain.attendance.application.exception.SessionNotFoundException import com.weeth.domain.attendance.domain.entity.Session import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse import com.weeth.domain.schedule.application.dto.response.SessionResponse -import com.weeth.domain.schedule.application.exception.SessionNotFoundException import com.weeth.domain.schedule.application.mapper.SessionMapper import com.weeth.domain.user.domain.entity.enums.Role import com.weeth.domain.user.domain.service.UserGetService diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt index 0c59e875..a43451f0 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt @@ -1,11 +1,11 @@ package com.weeth.domain.attendance.presentation +import com.weeth.domain.attendance.application.exception.SessionErrorCode import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase import com.weeth.domain.attendance.application.usecase.query.GetSessionQueryService import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse -import com.weeth.domain.schedule.application.exception.SessionErrorCode import com.weeth.global.auth.annotation.CurrentUser import com.weeth.global.common.exception.ApiErrorCodeExample import com.weeth.global.common.response.CommonResponse diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt index b7c3b9d6..414ed981 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt @@ -1,8 +1,8 @@ package com.weeth.domain.attendance.presentation +import com.weeth.domain.attendance.application.exception.SessionErrorCode import com.weeth.domain.attendance.application.usecase.query.GetSessionQueryService import com.weeth.domain.schedule.application.dto.response.SessionResponse -import com.weeth.domain.schedule.application.exception.SessionErrorCode import com.weeth.global.auth.annotation.CurrentUser import com.weeth.global.common.exception.ApiErrorCodeExample import com.weeth.global.common.response.CommonResponse diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionNotFoundException.kt b/src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionNotFoundException.kt deleted file mode 100644 index 0d545603..00000000 --- a/src/main/kotlin/com/weeth/domain/schedule/application/exception/SessionNotFoundException.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.weeth.domain.schedule.application.exception - -import com.weeth.global.common.exception.BaseException - -class SessionNotFoundException : BaseException(SessionErrorCode.SESSION_NOT_FOUND) diff --git a/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt b/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt index 443733ee..18e2f6b2 100644 --- a/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt +++ b/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt @@ -2,11 +2,11 @@ package com.weeth.global.common.controller import com.weeth.domain.account.application.exception.AccountErrorCode import com.weeth.domain.attendance.application.exception.AttendanceErrorCode +import com.weeth.domain.attendance.application.exception.SessionErrorCode import com.weeth.domain.board.application.exception.BoardErrorCode import com.weeth.domain.comment.application.exception.CommentErrorCode import com.weeth.domain.penalty.application.exception.PenaltyErrorCode import com.weeth.domain.schedule.application.exception.EventErrorCode -import com.weeth.domain.schedule.application.exception.SessionErrorCode import com.weeth.domain.user.application.exception.UserErrorCode import com.weeth.global.auth.jwt.application.exception.JwtErrorCode import com.weeth.global.common.exception.ApiErrorCodeExample @@ -28,7 +28,7 @@ class ExceptionDocController { @GetMapping("/attendance") @Operation(summary = "Attendance 도메인 에러 코드 목록") - @ApiErrorCodeExample(AttendanceErrorCode::class) + @ApiErrorCodeExample(AttendanceErrorCode::class, SessionErrorCode::class) fun attendanceErrorCodes() { } @@ -46,7 +46,7 @@ class ExceptionDocController { @GetMapping("/schedule") @Operation(summary = "Schedule 도메인 에러 코드 목록") - @ApiErrorCodeExample(EventErrorCode::class, SessionErrorCode::class) + @ApiErrorCodeExample(EventErrorCode::class) fun scheduleErrorCodes() { } From bfc1db0b9db9e8dd08e68c9468fbe0d6118b9429 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 24 Feb 2026 17:15:37 +0900 Subject: [PATCH 59/62] =?UTF-8?q?refactor:=20content=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=20text=20->=20length=20500=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/weeth/domain/attendance/domain/entity/Session.kt | 2 +- .../schedule/application/dto/request/ScheduleSaveRequest.kt | 2 ++ .../schedule/application/dto/request/ScheduleUpdateRequest.kt | 2 ++ .../kotlin/com/weeth/domain/schedule/domain/entity/Event.kt | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt index a552fdb6..4313720a 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt @@ -20,7 +20,7 @@ import java.time.LocalDateTime @Table(name = "meeting") class Session( var title: String, - @Column(columnDefinition = "TEXT") + @Column(length = 500) var content: String? = null, var location: String? = null, var cardinal: Int, diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt index c9db649e..942a6608 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleSaveRequest.kt @@ -3,6 +3,7 @@ package com.weeth.domain.schedule.application.dto.request import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Size import org.springframework.format.annotation.DateTimeFormat import java.time.LocalDateTime @@ -12,6 +13,7 @@ data class ScheduleSaveRequest( val title: String, @field:Schema(description = "일정 내용", example = "1박 2일 MT입니다.") @field:NotBlank + @field:Size(max = 500) val content: String, @field:Schema(description = "장소", example = "가평") @field:NotBlank diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt index f001d161..e9694e21 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/dto/request/ScheduleUpdateRequest.kt @@ -3,6 +3,7 @@ package com.weeth.domain.schedule.application.dto.request import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Size import org.springframework.format.annotation.DateTimeFormat import java.time.LocalDateTime @@ -12,6 +13,7 @@ data class ScheduleUpdateRequest( val title: String, @field:Schema(description = "일정 내용", example = "1박 2일 MT입니다.") @field:NotBlank + @field:Size(max = 500) val content: String, @field:Schema(description = "장소", example = "가평") @field:NotBlank diff --git a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt index 0efd8791..3bfdd4e6 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/domain/entity/Event.kt @@ -15,7 +15,7 @@ import java.time.LocalDateTime @Entity class Event( var title: String, - @Column(columnDefinition = "TEXT") + @Column(length = 500) var content: String, var location: String, var cardinal: Int, From 297e66d17b038afa54a1f9abd8bd4bfc106f0f0b Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 27 Feb 2026 16:12:45 +0900 Subject: [PATCH 60/62] =?UTF-8?q?refactor:=20attendance=EC=97=90=EC=84=9C?= =?UTF-8?q?=20session=20=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/ManageAttendanceUseCase.kt | 12 +- .../query/GetAttendanceQueryService.kt | 7 +- .../attendance/domain/entity/Attendance.kt | 1 + .../domain/entity/enums/SessionStatus.kt | 6 - .../domain/repository/AttendanceRepository.kt | 2 +- .../domain/repository/SessionReader.kt | 16 -- .../presentation/AttendanceResponseCode.kt | 15 +- .../application/mapper/ScheduleMapper.kt | 2 +- .../application/mapper/SessionMapper.kt | 2 +- .../usecase/query/GetScheduleQueryService.kt | 8 +- .../application/exception/SessionErrorCode.kt | 2 +- .../exception/SessionNotFoundException.kt | 2 +- .../usecase/command/ManageSessionUseCase.kt | 8 +- .../usecase/query/GetSessionQueryService.kt | 8 +- .../domain/entity/Session.kt | 186 +++++++++--------- .../domain/entity/enums/SessionStatus.kt | 6 + .../domain/repository/SessionReader.kt | 31 +++ .../domain/repository/SessionRepository.kt | 15 +- .../presentation/SessionAdminController.kt | 19 +- .../presentation/SessionController.kt | 8 +- .../presentation/SessionResponseCode.kt | 19 ++ .../usecase/command/AdminUserUseCase.kt | 2 +- .../controller/ExceptionDocController.kt | 2 +- .../mapper/AttendanceMapperTest.kt | 2 +- .../query/GetAttendanceQueryServiceTest.kt | 11 +- .../domain/entity/AttendanceTest.kt | 2 +- .../repository/AttendanceRepositoryTest.kt | 5 +- .../fixture/AttendanceTestFixture.kt | 33 +--- .../schedule/fixture/ScheduleTestFixture.kt | 28 --- .../domain/entity/SessionTest.kt | 10 +- .../session/fixture/SessionTestFixture.kt | 64 ++++++ .../usecase/command/AdminUserUseCaseTest.kt | 4 +- 32 files changed, 287 insertions(+), 251 deletions(-) delete mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/SessionStatus.kt delete mode 100644 src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt rename src/main/kotlin/com/weeth/domain/{attendance => session}/application/exception/SessionErrorCode.kt (92%) rename src/main/kotlin/com/weeth/domain/{attendance => session}/application/exception/SessionNotFoundException.kt (70%) rename src/main/kotlin/com/weeth/domain/{attendance => session}/application/usecase/command/ManageSessionUseCase.kt (88%) rename src/main/kotlin/com/weeth/domain/{attendance => session}/application/usecase/query/GetSessionQueryService.kt (88%) rename src/main/kotlin/com/weeth/domain/{attendance => session}/domain/entity/Session.kt (93%) create mode 100644 src/main/kotlin/com/weeth/domain/session/domain/entity/enums/SessionStatus.kt create mode 100644 src/main/kotlin/com/weeth/domain/session/domain/repository/SessionReader.kt rename src/main/kotlin/com/weeth/domain/{attendance => session}/domain/repository/SessionRepository.kt (67%) rename src/main/kotlin/com/weeth/domain/{attendance => session}/presentation/SessionAdminController.kt (78%) rename src/main/kotlin/com/weeth/domain/{attendance => session}/presentation/SessionController.kt (76%) create mode 100644 src/main/kotlin/com/weeth/domain/session/presentation/SessionResponseCode.kt rename src/test/kotlin/com/weeth/domain/{attendance => session}/domain/entity/SessionTest.kt (58%) create mode 100644 src/test/kotlin/com/weeth/domain/session/fixture/SessionTestFixture.kt diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt index 5c2be272..fcdfee1d 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageAttendanceUseCase.kt @@ -3,12 +3,12 @@ package com.weeth.domain.attendance.application.usecase.command import com.weeth.domain.attendance.application.dto.request.UpdateAttendanceStatusRequest import com.weeth.domain.attendance.application.exception.AttendanceCodeMismatchException import com.weeth.domain.attendance.application.exception.AttendanceNotFoundException -import com.weeth.domain.attendance.application.exception.SessionNotFoundException import com.weeth.domain.attendance.domain.entity.Attendance import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus -import com.weeth.domain.attendance.domain.entity.enums.SessionStatus import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.attendance.domain.repository.SessionRepository +import com.weeth.domain.session.application.exception.SessionNotFoundException +import com.weeth.domain.session.domain.entity.enums.SessionStatus +import com.weeth.domain.session.domain.repository.SessionReader import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.repository.UserReader import org.springframework.stereotype.Service @@ -19,7 +19,7 @@ import java.time.LocalDateTime @Service class ManageAttendanceUseCase( private val userReader: UserReader, - private val sessionRepository: SessionRepository, + private val sessionReader: SessionReader, private val attendanceRepository: AttendanceRepository, ) { @Transactional @@ -50,7 +50,7 @@ class ManageAttendanceUseCase( cardinal: Int, ) { val targetSession = - sessionRepository + sessionReader .findAllByCardinalOrderByStartAsc(cardinal) .firstOrNull { session -> session.start.toLocalDate().isEqual(now) && session.end.toLocalDate().isEqual(now) } ?: throw SessionNotFoundException() @@ -60,7 +60,7 @@ class ManageAttendanceUseCase( @Transactional fun autoClose() { - val sessions = sessionRepository.findAllByStatusAndEndBeforeOrderByEndAsc(SessionStatus.OPEN, LocalDateTime.now()) + val sessions = sessionReader.findAllByStatusAndEndBeforeOrderByEndAsc(SessionStatus.OPEN, LocalDateTime.now()) sessions.forEach { session -> session.close() val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt index a3f18ac5..9da8d254 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryService.kt @@ -3,10 +3,9 @@ package com.weeth.domain.attendance.application.usecase.query import com.weeth.domain.attendance.application.dto.response.AttendanceDetailResponse import com.weeth.domain.attendance.application.dto.response.AttendanceInfoResponse import com.weeth.domain.attendance.application.dto.response.AttendanceSummaryResponse -import com.weeth.domain.attendance.application.exception.SessionNotFoundException import com.weeth.domain.attendance.application.mapper.AttendanceMapper import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.attendance.domain.repository.SessionRepository +import com.weeth.domain.session.domain.repository.SessionReader import com.weeth.domain.user.domain.entity.enums.Role import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.repository.UserReader @@ -20,7 +19,7 @@ import java.time.LocalDate class GetAttendanceQueryService( private val userReader: UserReader, private val userCardinalPolicy: UserCardinalPolicy, - private val sessionRepository: SessionRepository, + private val sessionReader: SessionReader, private val attendanceRepository: AttendanceRepository, private val attendanceMapper: AttendanceMapper, ) { @@ -51,7 +50,7 @@ class GetAttendanceQueryService( } fun findAllAttendanceBySession(sessionId: Long): List { - val session = sessionRepository.findById(sessionId).orElseThrow { SessionNotFoundException() } + val session = sessionReader.getById(sessionId) val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) return attendances.map(attendanceMapper::toInfoResponse) } diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt index 7d58e2e1..be54e167 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Attendance.kt @@ -1,6 +1,7 @@ package com.weeth.domain.attendance.domain.entity import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus +import com.weeth.domain.session.domain.entity.Session import com.weeth.domain.user.domain.entity.User import com.weeth.global.common.entity.BaseEntity import jakarta.persistence.Column diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/SessionStatus.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/SessionStatus.kt deleted file mode 100644 index ee4da296..00000000 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/enums/SessionStatus.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.weeth.domain.attendance.domain.entity.enums - -enum class SessionStatus { - OPEN, - CLOSED, -} diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt index 6c682427..dec5d6bf 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt @@ -1,7 +1,7 @@ package com.weeth.domain.attendance.domain.repository import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.Session +import com.weeth.domain.session.domain.entity.Session import com.weeth.domain.user.domain.entity.User import com.weeth.domain.user.domain.entity.enums.Status import jakarta.persistence.LockModeType diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt deleted file mode 100644 index 30dd1dd1..00000000 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionReader.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.weeth.domain.attendance.domain.repository - -import com.weeth.domain.attendance.domain.entity.Session -import java.time.LocalDateTime - -// TODO: QR 코드 출석 기능 구현 시 사용 예정 (현재 시간 기준 진행 중인 세션 조회) -interface SessionReader { - fun findAllByStartBetween( - start: LocalDateTime, - end: LocalDateTime, - ): List - - fun findAllByCardinal(cardinal: Int): List - - fun findAllByCardinalIn(cardinals: List): List -} diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt index 651bd524..e67ef08a 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/presentation/AttendanceResponseCode.kt @@ -12,18 +12,9 @@ enum class AttendanceResponseCode( ATTENDANCE_CLOSE_SUCCESS(1200, HttpStatus.OK, "출석이 성공적으로 마감되었습니다."), ATTENDANCE_UPDATED_SUCCESS(1201, HttpStatus.OK, "개별 출석 상태가 성공적으로 수정되었습니다."), ATTENDANCE_FIND_DETAIL_SUCCESS(1202, HttpStatus.OK, "모든 인원의 정기모임 출석 정보가 성공적으로 조회되었습니다."), - SESSION_INFOS_FIND_SUCCESS(1203, HttpStatus.OK, "기수별 정기모임 리스트를 성공적으로 조회했습니다."), // AttendanceController 관련 - ATTENDANCE_CHECKIN_SUCCESS(1204, HttpStatus.OK, "출석이 성공적으로 처리되었습니다."), - ATTENDANCE_FIND_SUCCESS(1205, HttpStatus.OK, "사용자의 출석 정보가 성공적으로 조회되었습니다."), - ATTENDANCE_FIND_ALL_SUCCESS(1206, HttpStatus.OK, "사용자의 상세 출석 정보가 성공적으로 조회되었습니다."), - - // SessionAdminController 관련 - SESSION_SAVE_SUCCESS(1207, HttpStatus.OK, "정기모임이 성공적으로 생성되었습니다."), - SESSION_UPDATE_SUCCESS(1208, HttpStatus.OK, "정기모임이 성공적으로 수정되었습니다."), - SESSION_DELETE_SUCCESS(1209, HttpStatus.OK, "정기모임이 성공적으로 삭제되었습니다."), - - // SessionController 관련 - SESSION_FIND_SUCCESS(1210, HttpStatus.OK, "정기모임이 성공적으로 조회되었습니다."), + ATTENDANCE_CHECKIN_SUCCESS(1203, HttpStatus.OK, "출석이 성공적으로 처리되었습니다."), + ATTENDANCE_FIND_SUCCESS(1204, HttpStatus.OK, "사용자의 출석 정보가 성공적으로 조회되었습니다."), + ATTENDANCE_FIND_ALL_SUCCESS(1205, HttpStatus.OK, "사용자의 상세 출석 정보가 성공적으로 조회되었습니다."), } diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt index 628ab3d8..efe5ac27 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/ScheduleMapper.kt @@ -1,8 +1,8 @@ package com.weeth.domain.schedule.application.mapper -import com.weeth.domain.attendance.domain.entity.Session import com.weeth.domain.schedule.application.dto.response.ScheduleResponse import com.weeth.domain.schedule.domain.entity.Event +import com.weeth.domain.session.domain.entity.Session import org.springframework.stereotype.Component @Component diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt index 0a8ff978..8d587724 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/mapper/SessionMapper.kt @@ -1,11 +1,11 @@ package com.weeth.domain.schedule.application.mapper -import com.weeth.domain.attendance.domain.entity.Session import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest import com.weeth.domain.schedule.application.dto.response.SessionInfoResponse import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse import com.weeth.domain.schedule.application.dto.response.SessionResponse import com.weeth.domain.schedule.domain.entity.enums.Type +import com.weeth.domain.session.domain.entity.Session import com.weeth.domain.user.domain.entity.User import org.springframework.stereotype.Component diff --git a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt index 6e0a5105..e07112b8 100644 --- a/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/schedule/application/usecase/query/GetScheduleQueryService.kt @@ -1,12 +1,12 @@ package com.weeth.domain.schedule.application.usecase.query -import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.schedule.application.dto.response.EventResponse import com.weeth.domain.schedule.application.dto.response.ScheduleResponse import com.weeth.domain.schedule.application.exception.EventNotFoundException import com.weeth.domain.schedule.application.mapper.EventMapper import com.weeth.domain.schedule.application.mapper.ScheduleMapper import com.weeth.domain.schedule.domain.repository.EventRepository +import com.weeth.domain.session.domain.repository.SessionReader import com.weeth.domain.user.domain.repository.CardinalReader import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service @@ -17,7 +17,7 @@ import java.time.LocalDateTime @Transactional(readOnly = true) class GetScheduleQueryService( private val eventRepository: EventRepository, - private val sessionRepository: SessionRepository, + private val sessionReader: SessionReader, private val cardinalReader: CardinalReader, private val scheduleMapper: ScheduleMapper, private val eventMapper: EventMapper, @@ -37,7 +37,7 @@ class GetScheduleQueryService( .findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start) .map { scheduleMapper.toResponse(it, false) } val sessions = - sessionRepository + sessionReader .findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start) .map { scheduleMapper.toResponse(it, true) } return (events + sessions).sortedBy { it.start } @@ -53,7 +53,7 @@ class GetScheduleQueryService( .findAllByCardinal(cardinal.cardinalNumber) .map { scheduleMapper.toResponse(it, false) } val sessions = - sessionRepository + sessionReader .findAllByCardinal(cardinal.cardinalNumber) .map { scheduleMapper.toResponse(it, true) } diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionErrorCode.kt b/src/main/kotlin/com/weeth/domain/session/application/exception/SessionErrorCode.kt similarity index 92% rename from src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionErrorCode.kt rename to src/main/kotlin/com/weeth/domain/session/application/exception/SessionErrorCode.kt index 5b746258..a965bf4a 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionErrorCode.kt +++ b/src/main/kotlin/com/weeth/domain/session/application/exception/SessionErrorCode.kt @@ -1,4 +1,4 @@ -package com.weeth.domain.attendance.application.exception +package com.weeth.domain.session.application.exception import com.weeth.global.common.exception.ErrorCodeInterface import com.weeth.global.common.exception.ExplainError diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionNotFoundException.kt b/src/main/kotlin/com/weeth/domain/session/application/exception/SessionNotFoundException.kt similarity index 70% rename from src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionNotFoundException.kt rename to src/main/kotlin/com/weeth/domain/session/application/exception/SessionNotFoundException.kt index 027e0bf7..8e866880 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/exception/SessionNotFoundException.kt +++ b/src/main/kotlin/com/weeth/domain/session/application/exception/SessionNotFoundException.kt @@ -1,4 +1,4 @@ -package com.weeth.domain.attendance.application.exception +package com.weeth.domain.session.application.exception import com.weeth.global.common.exception.BaseException diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt b/src/main/kotlin/com/weeth/domain/session/application/usecase/command/ManageSessionUseCase.kt similarity index 88% rename from src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt rename to src/main/kotlin/com/weeth/domain/session/application/usecase/command/ManageSessionUseCase.kt index 96746e77..0d9d6c5a 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/command/ManageSessionUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/session/application/usecase/command/ManageSessionUseCase.kt @@ -1,13 +1,13 @@ -package com.weeth.domain.attendance.application.usecase.command +package com.weeth.domain.session.application.usecase.command -import com.weeth.domain.attendance.application.exception.SessionNotFoundException import com.weeth.domain.attendance.domain.entity.Attendance import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest import com.weeth.domain.schedule.application.mapper.SessionMapper +import com.weeth.domain.session.application.exception.SessionNotFoundException +import com.weeth.domain.session.domain.repository.SessionRepository import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.repository.CardinalReader import com.weeth.domain.user.domain.repository.UserReader @@ -32,7 +32,7 @@ class ManageSessionUseCase( val users = userReader.findAllByCardinalAndStatus(cardinal, Status.ACTIVE) val session = sessionMapper.toEntity(request, user) sessionRepository.save(session) - attendanceRepository.saveAll(users.map { Attendance.create(session, it) }) + attendanceRepository.saveAll(users.map { Attendance.Companion.create(session, it) }) } @Transactional diff --git a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt b/src/main/kotlin/com/weeth/domain/session/application/usecase/query/GetSessionQueryService.kt similarity index 88% rename from src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt rename to src/main/kotlin/com/weeth/domain/session/application/usecase/query/GetSessionQueryService.kt index b9bf8fc1..45a37895 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/application/usecase/query/GetSessionQueryService.kt +++ b/src/main/kotlin/com/weeth/domain/session/application/usecase/query/GetSessionQueryService.kt @@ -1,11 +1,11 @@ -package com.weeth.domain.attendance.application.usecase.query +package com.weeth.domain.session.application.usecase.query -import com.weeth.domain.attendance.application.exception.SessionNotFoundException -import com.weeth.domain.attendance.domain.entity.Session -import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse import com.weeth.domain.schedule.application.dto.response.SessionResponse import com.weeth.domain.schedule.application.mapper.SessionMapper +import com.weeth.domain.session.application.exception.SessionNotFoundException +import com.weeth.domain.session.domain.entity.Session +import com.weeth.domain.session.domain.repository.SessionRepository import com.weeth.domain.user.domain.entity.enums.Role import com.weeth.domain.user.domain.repository.UserReader import org.springframework.data.repository.findByIdOrNull diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt b/src/main/kotlin/com/weeth/domain/session/domain/entity/Session.kt similarity index 93% rename from src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt rename to src/main/kotlin/com/weeth/domain/session/domain/entity/Session.kt index 4313720a..09fd0f03 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/entity/Session.kt +++ b/src/main/kotlin/com/weeth/domain/session/domain/entity/Session.kt @@ -1,93 +1,93 @@ -package com.weeth.domain.attendance.domain.entity - -import com.weeth.domain.attendance.domain.entity.enums.SessionStatus -import com.weeth.domain.user.domain.entity.User -import com.weeth.global.common.entity.BaseEntity -import jakarta.persistence.Column -import jakarta.persistence.Entity -import jakarta.persistence.EnumType -import jakarta.persistence.Enumerated -import jakarta.persistence.FetchType -import jakarta.persistence.GeneratedValue -import jakarta.persistence.GenerationType -import jakarta.persistence.Id -import jakarta.persistence.JoinColumn -import jakarta.persistence.ManyToOne -import jakarta.persistence.Table -import java.time.LocalDateTime - -@Entity -@Table(name = "meeting") -class Session( - var title: String, - @Column(length = 500) - var content: String? = null, - var location: String? = null, - var cardinal: Int, - var start: LocalDateTime, - var end: LocalDateTime, - var code: Int, - @Enumerated(EnumType.STRING) - var status: SessionStatus = SessionStatus.OPEN, - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - var user: User? = null, -) : BaseEntity() { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - val id: Long = 0 - - fun close() { - check(status == SessionStatus.OPEN) { "이미 종료된 세션입니다" } - status = SessionStatus.CLOSED - } - - fun updateInfo( - title: String, - content: String?, - location: String?, - start: LocalDateTime, - end: LocalDateTime, - user: User?, - ) { - require(title.isNotBlank()) { "제목은 필수입니다" } - require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" } - this.title = title - this.content = content - this.location = location - this.start = start - this.end = end - this.user = user - } - - fun isCodeMatch(code: Int): Boolean = this.code == code - - fun isInProgress(now: LocalDateTime): Boolean = !now.isBefore(start) && !now.isAfter(end) - - companion object { - fun create( - title: String, - content: String?, - location: String?, - cardinal: Int, - start: LocalDateTime, - end: LocalDateTime, - user: User?, - ): Session { - require(title.isNotBlank()) { "제목은 필수입니다" } - require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" } - return Session( - title = title, - content = content, - location = location, - cardinal = cardinal, - start = start, - end = end, - code = generateCode(), - user = user, - ) - } - - private fun generateCode(): Int = (1000..9999).random() - } -} +package com.weeth.domain.session.domain.entity + +import com.weeth.domain.session.domain.entity.enums.SessionStatus +import com.weeth.domain.user.domain.entity.User +import com.weeth.global.common.entity.BaseEntity +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import java.time.LocalDateTime + +@Entity +@Table(name = "meeting") +class Session( + var title: String, + @Column(length = 500) + var content: String? = null, + var location: String? = null, + var cardinal: Int, + var start: LocalDateTime, + var end: LocalDateTime, + var code: Int, + @Enumerated(EnumType.STRING) + var status: SessionStatus = SessionStatus.OPEN, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + var user: User? = null, +) : BaseEntity() { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long = 0 + + fun close() { + check(status == SessionStatus.OPEN) { "이미 종료된 세션입니다" } + status = SessionStatus.CLOSED + } + + fun updateInfo( + title: String, + content: String?, + location: String?, + start: LocalDateTime, + end: LocalDateTime, + user: User?, + ) { + require(title.isNotBlank()) { "제목은 필수입니다" } + require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" } + this.title = title + this.content = content + this.location = location + this.start = start + this.end = end + this.user = user + } + + fun isCodeMatch(code: Int): Boolean = this.code == code + + fun isInProgress(now: LocalDateTime): Boolean = !now.isBefore(start) && !now.isAfter(end) + + companion object { + fun create( + title: String, + content: String?, + location: String?, + cardinal: Int, + start: LocalDateTime, + end: LocalDateTime, + user: User?, + ): Session { + require(title.isNotBlank()) { "제목은 필수입니다" } + require(!end.isBefore(start)) { "종료 시간은 시작 시간 이후여야 합니다" } + return Session( + title = title, + content = content, + location = location, + cardinal = cardinal, + start = start, + end = end, + code = generateCode(), + user = user, + ) + } + + private fun generateCode(): Int = (1000..9999).random() + } +} diff --git a/src/main/kotlin/com/weeth/domain/session/domain/entity/enums/SessionStatus.kt b/src/main/kotlin/com/weeth/domain/session/domain/entity/enums/SessionStatus.kt new file mode 100644 index 00000000..6f148579 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/session/domain/entity/enums/SessionStatus.kt @@ -0,0 +1,6 @@ +package com.weeth.domain.session.domain.entity.enums + +enum class SessionStatus { + OPEN, + CLOSED, +} diff --git a/src/main/kotlin/com/weeth/domain/session/domain/repository/SessionReader.kt b/src/main/kotlin/com/weeth/domain/session/domain/repository/SessionReader.kt new file mode 100644 index 00000000..430a3138 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/session/domain/repository/SessionReader.kt @@ -0,0 +1,31 @@ +package com.weeth.domain.session.domain.repository + +import com.weeth.domain.session.domain.entity.Session +import com.weeth.domain.session.domain.entity.enums.SessionStatus +import java.time.LocalDateTime + +interface SessionReader { + fun getById(sessionId: Long): Session + + // TODO: QR 코드 출석 기능 구현 시 사용 예정 (현재 시간 기준 진행 중인 세션 조회) + fun findAllByStartBetween( + start: LocalDateTime, + end: LocalDateTime, + ): List + + fun findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc( + end: LocalDateTime, + start: LocalDateTime, + ): List + + fun findAllByCardinal(cardinal: Int): List + + fun findAllByCardinalIn(cardinals: List): List + + fun findAllByCardinalOrderByStartAsc(cardinal: Int): List + + fun findAllByStatusAndEndBeforeOrderByEndAsc( + status: SessionStatus, + end: LocalDateTime, + ): List +} diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt b/src/main/kotlin/com/weeth/domain/session/domain/repository/SessionRepository.kt similarity index 67% rename from src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt rename to src/main/kotlin/com/weeth/domain/session/domain/repository/SessionRepository.kt index 2e955ee5..66c4c864 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/SessionRepository.kt +++ b/src/main/kotlin/com/weeth/domain/session/domain/repository/SessionRepository.kt @@ -1,7 +1,8 @@ -package com.weeth.domain.attendance.domain.repository +package com.weeth.domain.session.domain.repository -import com.weeth.domain.attendance.domain.entity.Session -import com.weeth.domain.attendance.domain.entity.enums.SessionStatus +import com.weeth.domain.session.application.exception.SessionNotFoundException +import com.weeth.domain.session.domain.entity.Session +import com.weeth.domain.session.domain.entity.enums.SessionStatus import jakarta.persistence.LockModeType import jakarta.persistence.QueryHint import org.springframework.data.jpa.repository.JpaRepository @@ -19,18 +20,18 @@ interface SessionRepository : @Query("SELECT s FROM Session s WHERE s.id = :id") fun findByIdWithLock(id: Long): Session? - fun findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc( + override fun findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc( end: LocalDateTime, start: LocalDateTime, ): List - fun findAllByCardinalOrderByStartAsc(cardinal: Int): List + override fun findAllByCardinalOrderByStartAsc(cardinal: Int): List fun findAllByCardinalOrderByStartDesc(cardinal: Int): List override fun findAllByCardinal(cardinal: Int): List - fun findAllByStatusAndEndBeforeOrderByEndAsc( + override fun findAllByStatusAndEndBeforeOrderByEndAsc( status: SessionStatus, end: LocalDateTime, ): List @@ -41,4 +42,6 @@ interface SessionRepository : override fun findAllByCardinalIn( @Param("cardinals") cardinals: List, ): List + + override fun getById(sessionId: Long): Session = findById(sessionId).orElseThrow { SessionNotFoundException() } } diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt b/src/main/kotlin/com/weeth/domain/session/presentation/SessionAdminController.kt similarity index 78% rename from src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt rename to src/main/kotlin/com/weeth/domain/session/presentation/SessionAdminController.kt index a43451f0..06e2a3ee 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionAdminController.kt +++ b/src/main/kotlin/com/weeth/domain/session/presentation/SessionAdminController.kt @@ -1,11 +1,11 @@ -package com.weeth.domain.attendance.presentation +package com.weeth.domain.session.presentation -import com.weeth.domain.attendance.application.exception.SessionErrorCode -import com.weeth.domain.attendance.application.usecase.command.ManageSessionUseCase -import com.weeth.domain.attendance.application.usecase.query.GetSessionQueryService import com.weeth.domain.schedule.application.dto.request.ScheduleSaveRequest import com.weeth.domain.schedule.application.dto.request.ScheduleUpdateRequest import com.weeth.domain.schedule.application.dto.response.SessionInfosResponse +import com.weeth.domain.session.application.exception.SessionErrorCode +import com.weeth.domain.session.application.usecase.command.ManageSessionUseCase +import com.weeth.domain.session.application.usecase.query.GetSessionQueryService import com.weeth.global.auth.annotation.CurrentUser import com.weeth.global.common.exception.ApiErrorCodeExample import com.weeth.global.common.response.CommonResponse @@ -38,7 +38,7 @@ class SessionAdminController( @Parameter(hidden = true) @CurrentUser userId: Long, ): CommonResponse { manageSessionUseCase.create(dto, userId) - return CommonResponse.success(AttendanceResponseCode.SESSION_SAVE_SUCCESS) + return CommonResponse.success(SessionResponseCode.SESSION_SAVE_SUCCESS) } @PatchMapping("/{sessionId}") @@ -49,7 +49,7 @@ class SessionAdminController( @Parameter(hidden = true) @CurrentUser userId: Long, ): CommonResponse { manageSessionUseCase.update(sessionId, dto, userId) - return CommonResponse.success(AttendanceResponseCode.SESSION_UPDATE_SUCCESS) + return CommonResponse.success(SessionResponseCode.SESSION_UPDATE_SUCCESS) } @DeleteMapping("/{sessionId}") @@ -58,7 +58,7 @@ class SessionAdminController( @PathVariable sessionId: Long, ): CommonResponse { manageSessionUseCase.delete(sessionId) - return CommonResponse.success(AttendanceResponseCode.SESSION_DELETE_SUCCESS) + return CommonResponse.success(SessionResponseCode.SESSION_DELETE_SUCCESS) } @GetMapping @@ -66,5 +66,8 @@ class SessionAdminController( fun getSessionInfos( @RequestParam(required = false) cardinal: Int?, ): CommonResponse = - CommonResponse.success(AttendanceResponseCode.SESSION_INFOS_FIND_SUCCESS, getSessionQueryService.findSessionInfos(cardinal)) + CommonResponse.success( + SessionResponseCode.SESSION_INFOS_FIND_SUCCESS, + getSessionQueryService.findSessionInfos(cardinal), + ) } diff --git a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt b/src/main/kotlin/com/weeth/domain/session/presentation/SessionController.kt similarity index 76% rename from src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt rename to src/main/kotlin/com/weeth/domain/session/presentation/SessionController.kt index 414ed981..310282b1 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/presentation/SessionController.kt +++ b/src/main/kotlin/com/weeth/domain/session/presentation/SessionController.kt @@ -1,8 +1,8 @@ -package com.weeth.domain.attendance.presentation +package com.weeth.domain.session.presentation -import com.weeth.domain.attendance.application.exception.SessionErrorCode -import com.weeth.domain.attendance.application.usecase.query.GetSessionQueryService import com.weeth.domain.schedule.application.dto.response.SessionResponse +import com.weeth.domain.session.application.exception.SessionErrorCode +import com.weeth.domain.session.application.usecase.query.GetSessionQueryService import com.weeth.global.auth.annotation.CurrentUser import com.weeth.global.common.exception.ApiErrorCodeExample import com.weeth.global.common.response.CommonResponse @@ -27,5 +27,5 @@ class SessionController( @Parameter(hidden = true) @CurrentUser userId: Long, @PathVariable sessionId: Long, ): CommonResponse = - CommonResponse.success(AttendanceResponseCode.SESSION_FIND_SUCCESS, getSessionQueryService.findSession(userId, sessionId)) + CommonResponse.success(SessionResponseCode.SESSION_FIND_SUCCESS, getSessionQueryService.findSession(userId, sessionId)) } diff --git a/src/main/kotlin/com/weeth/domain/session/presentation/SessionResponseCode.kt b/src/main/kotlin/com/weeth/domain/session/presentation/SessionResponseCode.kt new file mode 100644 index 00000000..78f20ead --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/session/presentation/SessionResponseCode.kt @@ -0,0 +1,19 @@ +package com.weeth.domain.session.presentation + +import com.weeth.global.common.response.ResponseCodeInterface +import org.springframework.http.HttpStatus + +enum class SessionResponseCode( + override val code: Int, + override val status: HttpStatus, + override val message: String, +) : ResponseCodeInterface { + // SessionAdminController 관련 + SESSION_INFOS_FIND_SUCCESS(1206, HttpStatus.OK, "기수별 정기모임 리스트를 성공적으로 조회했습니다."), + SESSION_SAVE_SUCCESS(1207, HttpStatus.OK, "정기모임이 성공적으로 생성되었습니다."), + SESSION_UPDATE_SUCCESS(1208, HttpStatus.OK, "정기모임이 성공적으로 수정되었습니다."), + SESSION_DELETE_SUCCESS(1209, HttpStatus.OK, "정기모임이 성공적으로 삭제되었습니다."), + + // SessionController 관련 + SESSION_FIND_SUCCESS(1210, HttpStatus.OK, "정기모임이 성공적으로 조회되었습니다."), +} diff --git a/src/main/kotlin/com/weeth/domain/user/application/usecase/command/AdminUserUseCase.kt b/src/main/kotlin/com/weeth/domain/user/application/usecase/command/AdminUserUseCase.kt index b39137f9..af9fccfb 100644 --- a/src/main/kotlin/com/weeth/domain/user/application/usecase/command/AdminUserUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/user/application/usecase/command/AdminUserUseCase.kt @@ -2,7 +2,7 @@ package com.weeth.domain.user.application.usecase.command import com.weeth.domain.attendance.domain.entity.Attendance import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.attendance.domain.repository.SessionReader +import com.weeth.domain.session.domain.repository.SessionReader import com.weeth.domain.user.application.dto.request.UserApplyObRequest import com.weeth.domain.user.application.dto.request.UserIdsRequest import com.weeth.domain.user.application.dto.request.UserRoleUpdateRequest diff --git a/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt b/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt index 18e2f6b2..1bc82ac0 100644 --- a/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt +++ b/src/main/kotlin/com/weeth/global/common/controller/ExceptionDocController.kt @@ -2,11 +2,11 @@ package com.weeth.global.common.controller import com.weeth.domain.account.application.exception.AccountErrorCode import com.weeth.domain.attendance.application.exception.AttendanceErrorCode -import com.weeth.domain.attendance.application.exception.SessionErrorCode import com.weeth.domain.board.application.exception.BoardErrorCode import com.weeth.domain.comment.application.exception.CommentErrorCode import com.weeth.domain.penalty.application.exception.PenaltyErrorCode import com.weeth.domain.schedule.application.exception.EventErrorCode +import com.weeth.domain.session.application.exception.SessionErrorCode import com.weeth.domain.user.application.exception.UserErrorCode import com.weeth.global.auth.jwt.application.exception.JwtErrorCode import com.weeth.global.common.exception.ApiErrorCodeExample diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt index 5f033383..700475d2 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/application/mapper/AttendanceMapperTest.kt @@ -3,10 +3,10 @@ package com.weeth.domain.attendance.application.mapper import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUser import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createAdminUser import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createAttendance -import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createOneDaySession import com.weeth.domain.attendance.fixture.AttendanceTestFixture.enrichUserProfile import com.weeth.domain.attendance.fixture.AttendanceTestFixture.setAttendanceId import com.weeth.domain.attendance.fixture.AttendanceTestFixture.setUserAttendanceStats +import com.weeth.domain.session.fixture.SessionTestFixture.createOneDaySession import io.kotest.core.spec.style.DescribeSpec import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.nulls.shouldNotBeNull diff --git a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt index a90c4e38..d098b66d 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/application/usecase/query/GetAttendanceQueryServiceTest.kt @@ -6,10 +6,10 @@ import com.weeth.domain.attendance.application.dto.response.AttendanceResponse import com.weeth.domain.attendance.application.dto.response.AttendanceSummaryResponse import com.weeth.domain.attendance.application.mapper.AttendanceMapper import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.Session import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.attendance.domain.repository.SessionRepository import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUser +import com.weeth.domain.session.domain.entity.Session +import com.weeth.domain.session.domain.repository.SessionReader import com.weeth.domain.user.domain.entity.Cardinal import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.repository.UserReader @@ -19,14 +19,13 @@ import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import io.mockk.verify -import java.util.Optional class GetAttendanceQueryServiceTest : DescribeSpec({ val userReader = mockk() val userCardinalPolicy = mockk() - val sessionRepository = mockk() + val sessionReader = mockk() val attendanceRepository = mockk() val attendanceMapper = mockk() @@ -34,7 +33,7 @@ class GetAttendanceQueryServiceTest : GetAttendanceQueryService( userReader, userCardinalPolicy, - sessionRepository, + sessionReader, attendanceRepository, attendanceMapper, ) @@ -113,7 +112,7 @@ class GetAttendanceQueryServiceTest : val response1 = mockk() val response2 = mockk() - every { sessionRepository.findById(sessionId) } returns Optional.of(session) + every { sessionReader.getById(sessionId) } returns session every { attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) } returns listOf(attendance1, attendance2) diff --git a/src/test/kotlin/com/weeth/domain/attendance/domain/entity/AttendanceTest.kt b/src/test/kotlin/com/weeth/domain/attendance/domain/entity/AttendanceTest.kt index 5158e028..f58eb13a 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/domain/entity/AttendanceTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/domain/entity/AttendanceTest.kt @@ -3,7 +3,7 @@ package com.weeth.domain.attendance.domain.entity import com.weeth.domain.attendance.domain.entity.enums.AttendanceStatus import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createActiveUser import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createAttendance -import com.weeth.domain.attendance.fixture.AttendanceTestFixture.createOneDaySession +import com.weeth.domain.session.fixture.SessionTestFixture.createOneDaySession import io.kotest.core.spec.style.DescribeSpec import io.kotest.matchers.shouldBe import java.time.LocalDate diff --git a/src/test/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepositoryTest.kt b/src/test/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepositoryTest.kt index ad781505..1b4fd3c8 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepositoryTest.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepositoryTest.kt @@ -2,8 +2,9 @@ package com.weeth.domain.attendance.domain.repository import com.weeth.config.TestContainersConfig import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.Session -import com.weeth.domain.attendance.domain.entity.enums.SessionStatus +import com.weeth.domain.session.domain.entity.Session +import com.weeth.domain.session.domain.entity.enums.SessionStatus +import com.weeth.domain.session.domain.repository.SessionRepository import com.weeth.domain.user.domain.entity.User import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.repository.UserRepository diff --git a/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt b/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt index f5fe971e..f20c8f7f 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt +++ b/src/test/kotlin/com/weeth/domain/attendance/fixture/AttendanceTestFixture.kt @@ -1,14 +1,12 @@ package com.weeth.domain.attendance.fixture import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.Session +import com.weeth.domain.session.domain.entity.Session import com.weeth.domain.user.domain.entity.User import com.weeth.domain.user.domain.entity.enums.Role import com.weeth.domain.user.domain.entity.enums.Status import com.weeth.domain.user.domain.vo.AttendanceStats import org.springframework.test.util.ReflectionTestUtils -import java.time.LocalDate -import java.time.LocalDateTime object AttendanceTestFixture { fun createActiveUser(name: String): User = @@ -29,35 +27,6 @@ object AttendanceTestFixture { user: User, ): Attendance = Attendance.create(session, user) - fun createOneDaySession( - date: LocalDate, - cardinal: Int, - code: Int, - title: String, - ): Session = - Session( - title = title, - location = "Test Location", - start = date.atTime(10, 0), - end = date.atTime(12, 0), - code = code, - cardinal = cardinal, - ) - - fun createInProgressSession( - cardinal: Int, - code: Int, - title: String, - ): Session = - Session( - title = title, - location = "Test Location", - start = LocalDateTime.now().minusMinutes(5), - end = LocalDateTime.now().plusMinutes(5), - code = code, - cardinal = cardinal, - ) - fun setAttendanceId( attendance: Attendance, id: Long, diff --git a/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt b/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt index d3b81085..39fb7995 100644 --- a/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt +++ b/src/test/kotlin/com/weeth/domain/schedule/fixture/ScheduleTestFixture.kt @@ -1,7 +1,5 @@ package com.weeth.domain.schedule.fixture -import com.weeth.domain.attendance.domain.entity.Session -import com.weeth.domain.attendance.domain.entity.enums.SessionStatus import com.weeth.domain.schedule.domain.entity.Event import org.springframework.test.util.ReflectionTestUtils import java.time.LocalDateTime @@ -29,30 +27,4 @@ object ScheduleTestFixture { if (id != 0L) ReflectionTestUtils.setField(event, "id", id) return event } - - fun createSession( - id: Long = 0L, - title: String = "Test Session", - content: String = "Test Content", - location: String = "Test Location", - cardinal: Int = 1, - code: Int = 1234, - status: SessionStatus = SessionStatus.OPEN, - start: LocalDateTime = LocalDateTime.of(2026, 3, 1, 10, 0), - end: LocalDateTime = LocalDateTime.of(2026, 3, 1, 12, 0), - ): Session { - val session = - Session( - title = title, - content = content, - location = location, - cardinal = cardinal, - code = code, - status = status, - start = start, - end = end, - ) - if (id != 0L) ReflectionTestUtils.setField(session, "id", id) - return session - } } diff --git a/src/test/kotlin/com/weeth/domain/attendance/domain/entity/SessionTest.kt b/src/test/kotlin/com/weeth/domain/session/domain/entity/SessionTest.kt similarity index 58% rename from src/test/kotlin/com/weeth/domain/attendance/domain/entity/SessionTest.kt rename to src/test/kotlin/com/weeth/domain/session/domain/entity/SessionTest.kt index 722e6253..22aa40b7 100644 --- a/src/test/kotlin/com/weeth/domain/attendance/domain/entity/SessionTest.kt +++ b/src/test/kotlin/com/weeth/domain/session/domain/entity/SessionTest.kt @@ -1,7 +1,7 @@ -package com.weeth.domain.attendance.domain.entity +package com.weeth.domain.session.domain.entity -import com.weeth.domain.attendance.domain.entity.enums.SessionStatus -import com.weeth.domain.schedule.fixture.ScheduleTestFixture +import com.weeth.domain.session.domain.entity.enums.SessionStatus +import com.weeth.domain.session.fixture.SessionTestFixture import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe @@ -9,7 +9,7 @@ import io.kotest.matchers.shouldBe class SessionTest : StringSpec({ "close는 status를 CLOSED로 변경한다" { - val session = ScheduleTestFixture.createSession(status = SessionStatus.OPEN) + val session = SessionTestFixture.createSession(status = SessionStatus.OPEN) session.close() @@ -17,7 +17,7 @@ class SessionTest : } "이미 CLOSED 상태에서 close 호출 시 예외가 발생한다" { - val session = ScheduleTestFixture.createSession(status = SessionStatus.CLOSED) + val session = SessionTestFixture.createSession(status = SessionStatus.CLOSED) shouldThrow { session.close() diff --git a/src/test/kotlin/com/weeth/domain/session/fixture/SessionTestFixture.kt b/src/test/kotlin/com/weeth/domain/session/fixture/SessionTestFixture.kt new file mode 100644 index 00000000..584c6b4e --- /dev/null +++ b/src/test/kotlin/com/weeth/domain/session/fixture/SessionTestFixture.kt @@ -0,0 +1,64 @@ +package com.weeth.domain.session.fixture + +import com.weeth.domain.session.domain.entity.Session +import com.weeth.domain.session.domain.entity.enums.SessionStatus +import org.springframework.test.util.ReflectionTestUtils +import java.time.LocalDate +import java.time.LocalDateTime + +object SessionTestFixture { + fun createSession( + id: Long = 0L, + title: String = "Test Session", + content: String = "Test Content", + location: String = "Test Location", + cardinal: Int = 1, + code: Int = 1234, + status: SessionStatus = SessionStatus.OPEN, + start: LocalDateTime = LocalDateTime.of(2026, 3, 1, 10, 0), + end: LocalDateTime = LocalDateTime.of(2026, 3, 1, 12, 0), + ): Session { + val session = + Session( + title = title, + content = content, + location = location, + cardinal = cardinal, + code = code, + status = status, + start = start, + end = end, + ) + if (id != 0L) ReflectionTestUtils.setField(session, "id", id) + return session + } + + fun createOneDaySession( + date: LocalDate, + cardinal: Int, + code: Int, + title: String, + ): Session = + Session( + title = title, + location = "Test Location", + start = date.atTime(10, 0), + end = date.atTime(12, 0), + code = code, + cardinal = cardinal, + ) + + fun createInProgressSession( + cardinal: Int, + code: Int, + title: String, + ): Session = + Session( + title = title, + location = "Test Location", + start = LocalDateTime.now().minusMinutes(5), + end = LocalDateTime.now().plusMinutes(5), + code = code, + cardinal = cardinal, + ) +} diff --git a/src/test/kotlin/com/weeth/domain/user/application/usecase/command/AdminUserUseCaseTest.kt b/src/test/kotlin/com/weeth/domain/user/application/usecase/command/AdminUserUseCaseTest.kt index 2eee56b7..06c279cf 100644 --- a/src/test/kotlin/com/weeth/domain/user/application/usecase/command/AdminUserUseCaseTest.kt +++ b/src/test/kotlin/com/weeth/domain/user/application/usecase/command/AdminUserUseCaseTest.kt @@ -1,9 +1,9 @@ package com.weeth.domain.user.application.usecase.command import com.weeth.domain.attendance.domain.entity.Attendance -import com.weeth.domain.attendance.domain.entity.Session import com.weeth.domain.attendance.domain.repository.AttendanceRepository -import com.weeth.domain.attendance.domain.repository.SessionReader +import com.weeth.domain.session.domain.entity.Session +import com.weeth.domain.session.domain.repository.SessionReader import com.weeth.domain.user.application.dto.request.UserApplyObRequest import com.weeth.domain.user.application.dto.request.UserIdsRequest import com.weeth.domain.user.application.dto.request.UserRoleUpdateRequest From 9cd5cee96bc322776c25e4ac29979a8785cfec2d Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 27 Feb 2026 16:15:33 +0900 Subject: [PATCH 61/62] =?UTF-8?q?refactor:=20SwaggerConfig=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=BD=94=EB=93=9C=20=EB=B2=94=EC=9C=84=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20Session=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/com/weeth/global/config/SwaggerConfig.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/weeth/global/config/SwaggerConfig.kt b/src/main/kotlin/com/weeth/global/config/SwaggerConfig.kt index a6763ffe..d174e817 100644 --- a/src/main/kotlin/com/weeth/global/config/SwaggerConfig.kt +++ b/src/main/kotlin/com/weeth/global/config/SwaggerConfig.kt @@ -33,7 +33,7 @@ private const val SWAGGER_DESCRIPTION = "| Domain | Success | Error |\n" + "|--------|---------|------|\n" + "| Account | 11xx | 21xx |\n" + - "| Attendance | 12xx | 22xx |\n" + + "| Attendance/Session | 12xx | 22xx |\n" + "| Board | 13xx | 23xx |\n" + "| Comment | 14xx | 24xx |\n" + "| File | 15xx | 25xx |\n" + From e0de65820b0c8ac903da7913e46f473052651876 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Fri, 27 Feb 2026 16:21:36 +0900 Subject: [PATCH 62/62] =?UTF-8?q?fix:=20=EC=84=B8=EC=85=98=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=8B=9C=20=EC=B6=9C=EC=84=9D=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=EC=97=90=20=EB=B9=84=EA=B4=80=EC=A0=81=20?= =?UTF-8?q?=EB=9D=BD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../attendance/domain/repository/AttendanceRepository.kt | 8 ++++++++ .../application/usecase/command/ManageSessionUseCase.kt | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt index dec5d6bf..96c162a8 100644 --- a/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt +++ b/src/main/kotlin/com/weeth/domain/attendance/domain/repository/AttendanceRepository.kt @@ -30,6 +30,14 @@ interface AttendanceRepository : JpaRepository { status: Status, ): List + @Lock(LockModeType.PESSIMISTIC_WRITE) + @QueryHints(QueryHint(name = "jakarta.persistence.lock.timeout", value = "2000")) + @Query("SELECT a FROM Attendance a JOIN FETCH a.user WHERE a.session = :session AND a.user.status = :status") + fun findAllBySessionAndUserStatusWithLock( + @Param("session") session: Session, + @Param("status") status: Status, + ): List + @Query("SELECT a FROM Attendance a JOIN FETCH a.user WHERE a.id = :id") fun findByIdWithUser(id: Long): Attendance? diff --git a/src/main/kotlin/com/weeth/domain/session/application/usecase/command/ManageSessionUseCase.kt b/src/main/kotlin/com/weeth/domain/session/application/usecase/command/ManageSessionUseCase.kt index 0d9d6c5a..965df991 100644 --- a/src/main/kotlin/com/weeth/domain/session/application/usecase/command/ManageSessionUseCase.kt +++ b/src/main/kotlin/com/weeth/domain/session/application/usecase/command/ManageSessionUseCase.kt @@ -49,7 +49,7 @@ class ManageSessionUseCase( @Transactional fun delete(sessionId: Long) { val session = sessionRepository.findByIdWithLock(sessionId) ?: throw SessionNotFoundException() - val attendances = attendanceRepository.findAllBySessionAndUserStatus(session, Status.ACTIVE) + val attendances = attendanceRepository.findAllBySessionAndUserStatusWithLock(session, Status.ACTIVE) attendances.forEach { a -> when (a.status) { AttendanceStatus.ATTEND -> a.user.removeAttend()