From 7d04d6369fcaac33d2963c188f992b2239747767 Mon Sep 17 00:00:00 2001 From: shane Date: Sun, 28 Apr 2024 22:20:10 +0900 Subject: [PATCH 1/4] feat: schedule search service interface --- .../schedule/repository/ScheduleRepository.kt | 6 ++ .../schedule/service/ScheduleSearchService.kt | 11 ++++ .../service/ScheduleSearchServiceDBImpl.kt | 51 ++++++++++++++++ .../ScheduleSearchServiceDBImplTest.kt | 58 +++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt create mode 100644 src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt create mode 100644 src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImplTest.kt diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/repository/ScheduleRepository.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/repository/ScheduleRepository.kt index d235a715..5cc4c74b 100644 --- a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/repository/ScheduleRepository.kt +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/repository/ScheduleRepository.kt @@ -10,6 +10,12 @@ import java.util.* interface ScheduleRepository : JpaRepository { +// fun findByMemberAndContentContainingAndVisibilityIn( +// member: Member, +// content: String, +// visibility: Collection +// ): List + @Query( "SELECT s" + " FROM Schedule s" + diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt new file mode 100644 index 00000000..635cd22f --- /dev/null +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt @@ -0,0 +1,11 @@ +package com.tistory.shanepark.dutypark.schedule.service + +import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleDto +import com.tistory.shanepark.dutypark.security.domain.dto.LoginMember +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable + +interface ScheduleSearchService { + + fun search(loginMember: LoginMember, targetMemberId: Long, page: Pageable, keyword: String): Page +} diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt new file mode 100644 index 00000000..66fe9d32 --- /dev/null +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt @@ -0,0 +1,51 @@ +package com.tistory.shanepark.dutypark.schedule.service + +import com.tistory.shanepark.dutypark.common.domain.dto.CalendarView +import com.tistory.shanepark.dutypark.member.domain.enums.Visibility +import com.tistory.shanepark.dutypark.member.repository.MemberRepository +import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleDto +import com.tistory.shanepark.dutypark.schedule.domain.entity.Schedule +import com.tistory.shanepark.dutypark.schedule.repository.ScheduleRepository +import com.tistory.shanepark.dutypark.security.domain.dto.LoginMember +import org.springframework.data.domain.Page +import org.springframework.data.domain.PageImpl +import org.springframework.data.domain.Pageable +import org.springframework.stereotype.Service +import java.time.LocalDateTime +import java.time.YearMonth + +/** + * When search engine is implemented, this service will be replaced with ScheduleSearchServiceESImpl + */ +@Service +class ScheduleSearchServiceDBImpl( + private val scheduleRepository: ScheduleRepository, + private val memberRepository: MemberRepository +) : ScheduleSearchService { + + override fun search( + loginMember: LoginMember, + targetMemberId: Long, + page: Pageable, + keyword: String + ): Page { + // 1. get proper auth level for targetMemberId + + // 2. search schedules on database and sort by date desc + + // 3. return Page + + val member = memberRepository.findById(loginMember.id).orElseThrow() + val now = LocalDateTime.now() + val s1 = scheduleRepository.save(Schedule(member, "test1", now, now, 0, Visibility.FRIENDS)) + val s2 = scheduleRepository.save(Schedule(member, "test2", now, now, 0, Visibility.FRIENDS)) + val s3 = scheduleRepository.save(Schedule(member, "test3", now, now, 0, Visibility.FRIENDS)) + + val calendarView = CalendarView(YearMonth.now()) + val s1_ = ScheduleDto.of(calendarView, s1, false)[0] + val s2_ = ScheduleDto.of(calendarView, s2, false)[0] + val s3_ = ScheduleDto.of(calendarView, s3, false)[0] + return PageImpl(listOf(s3_, s2_, s1_), page, 3) + } + +} diff --git a/src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImplTest.kt b/src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImplTest.kt new file mode 100644 index 00000000..7103c4cb --- /dev/null +++ b/src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImplTest.kt @@ -0,0 +1,58 @@ +package com.tistory.shanepark.dutypark.schedule.service + +import com.tistory.shanepark.dutypark.DutyparkIntegrationTest +import com.tistory.shanepark.dutypark.member.domain.enums.Visibility +import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleUpdateDto +import com.tistory.shanepark.dutypark.security.domain.dto.LoginMember +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.data.domain.Pageable +import java.time.LocalDateTime + +class ScheduleSearchServiceDBImplTest : DutyparkIntegrationTest() { + + @Autowired + lateinit var scheduleSearchServiceDBImpl: ScheduleSearchService + + @Autowired + lateinit var scheduleService: ScheduleService + + @Test + fun `search schedules 3 results, it should be sorted by date desc and paged`() { + val member = TestData.member + val loginMember = loginMember(member) + + // Given + makeSchedule(loginMember, "test1", LocalDateTime.of(2024, 1, 1, 0, 0)) + makeSchedule(loginMember, "test2", LocalDateTime.of(2024, 1, 2, 0, 0)) + makeSchedule(loginMember, "test3", LocalDateTime.of(2024, 1, 3, 0, 0)) + makeSchedule(loginMember, "sample1", LocalDateTime.of(2024, 1, 4, 0, 0)) + makeSchedule(loginMember, "sample2", LocalDateTime.of(2024, 1, 5, 0, 0)) + + // When + val result = scheduleSearchServiceDBImpl.search(loginMember, loginMember.id, Pageable.ofSize(10), "test") + + // Then + Assertions.assertThat(result).hasSize(3) + Assertions.assertThat(result.content.get(0).content).isEqualTo("test3") + Assertions.assertThat(result.content.get(1).content).isEqualTo("test2") + Assertions.assertThat(result.content.get(2).content).isEqualTo("test1") + } + + private fun makeSchedule( + loginMember: LoginMember, title: String, date: LocalDateTime + ) { + scheduleService.createSchedule( + loginMember, + ScheduleUpdateDto( + loginMember.id, + title, + Visibility.FRIENDS, + date, + date + ) + ) + } + +} From 7f56b456fb15fb1ecc86f4e43f26ee8af70e55fd Mon Sep 17 00:00:00 2001 From: shane Date: Fri, 17 May 2024 23:04:07 +0900 Subject: [PATCH 2/4] feat: implement ScheduleSearchServiceDBImpl class --- .../dutypark/member/service/FriendService.kt | 13 ++++ .../domain/dto/ScheduleSearchResult.kt | 24 +++++++ .../schedule/repository/ScheduleRepository.kt | 24 +++++-- .../schedule/service/ScheduleSearchService.kt | 4 +- .../service/ScheduleSearchServiceDBImpl.kt | 40 ++++------- .../schedule/service/ScheduleService.kt | 14 +--- .../dutypark/DutyparkIntegrationTest.kt | 10 +++ .../member/service/FriendServiceTest.kt | 3 - .../ScheduleSearchServiceDBImplTest.kt | 69 +++++++++++++++++-- .../schedule/service/ScheduleServiceTest.kt | 10 --- 10 files changed, 145 insertions(+), 66 deletions(-) create mode 100644 src/main/kotlin/com/tistory/shanepark/dutypark/schedule/domain/dto/ScheduleSearchResult.kt diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/member/service/FriendService.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/member/service/FriendService.kt index 11a368ed..90f276dc 100644 --- a/src/main/kotlin/com/tistory/shanepark/dutypark/member/service/FriendService.kt +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/member/service/FriendService.kt @@ -180,4 +180,17 @@ class FriendService( } } + @Transactional(readOnly = true) + fun availableVisibilities(loginMember: LoginMember?, member: Member): Set { + if (loginMember == null) + return setOf(Visibility.PUBLIC) + if (loginMember.id == member.id) { + return Visibility.values().toSet() + } + if (isFriend(loginMember.id, member.id!!)) { + return setOf(Visibility.PUBLIC, Visibility.FRIENDS) + } + return setOf(Visibility.PUBLIC) + } + } diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/domain/dto/ScheduleSearchResult.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/domain/dto/ScheduleSearchResult.kt new file mode 100644 index 00000000..6802c489 --- /dev/null +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/domain/dto/ScheduleSearchResult.kt @@ -0,0 +1,24 @@ +package com.tistory.shanepark.dutypark.schedule.domain.dto + +import com.tistory.shanepark.dutypark.schedule.domain.entity.Schedule +import java.time.LocalDateTime + +data class ScheduleSearchResult( + val content: String, + val startDateTime: LocalDateTime, + val endDateTime: LocalDateTime, + val visibility: String, + val isTagged: Boolean, +) { + companion object { + fun of(schedule: Schedule): ScheduleSearchResult { + return ScheduleSearchResult( + content = schedule.content, + startDateTime = schedule.startDateTime, + endDateTime = schedule.endDateTime, + visibility = schedule.visibility.name, + isTagged = schedule.tags.isNotEmpty() + ) + } + } +} diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/repository/ScheduleRepository.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/repository/ScheduleRepository.kt index 5cc4c74b..73fb8ceb 100644 --- a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/repository/ScheduleRepository.kt +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/repository/ScheduleRepository.kt @@ -3,6 +3,8 @@ package com.tistory.shanepark.dutypark.schedule.repository import com.tistory.shanepark.dutypark.member.domain.entity.Member import com.tistory.shanepark.dutypark.member.domain.enums.Visibility import com.tistory.shanepark.dutypark.schedule.domain.entity.Schedule +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query import java.time.LocalDateTime @@ -10,11 +12,23 @@ import java.util.* interface ScheduleRepository : JpaRepository { -// fun findByMemberAndContentContainingAndVisibilityIn( -// member: Member, -// content: String, -// visibility: Collection -// ): List + @Query( + "SELECT s" + + " FROM Schedule s" + + " JOIN FETCH s.member m" + + " LEFT JOIN FETCH s.tags t" + + " LEFT JOIN FETCH t.member tm" + + " WHERE (m = :member or tm = :member)" + + " AND s.content LIKE %:content%" + + " AND s.visibility IN (:visibility)" + + " ORDER BY s.startDateTime DESC" + ) + fun findByMemberAndContentContainingAndVisibilityIn( + member: Member, + content: String, + visibility: Collection, + pageable: Pageable, + ): Page @Query( "SELECT s" + diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt index 635cd22f..f8e0f1c9 100644 --- a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt @@ -1,11 +1,11 @@ package com.tistory.shanepark.dutypark.schedule.service -import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleDto +import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleSearchResult import com.tistory.shanepark.dutypark.security.domain.dto.LoginMember import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable interface ScheduleSearchService { - fun search(loginMember: LoginMember, targetMemberId: Long, page: Pageable, keyword: String): Page + fun search(loginMember: LoginMember, targetMemberId: Long, page: Pageable, keyword: String): Page } diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt index 66fe9d32..0665094e 100644 --- a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt @@ -1,18 +1,13 @@ package com.tistory.shanepark.dutypark.schedule.service -import com.tistory.shanepark.dutypark.common.domain.dto.CalendarView -import com.tistory.shanepark.dutypark.member.domain.enums.Visibility import com.tistory.shanepark.dutypark.member.repository.MemberRepository -import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleDto -import com.tistory.shanepark.dutypark.schedule.domain.entity.Schedule +import com.tistory.shanepark.dutypark.member.service.FriendService +import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleSearchResult import com.tistory.shanepark.dutypark.schedule.repository.ScheduleRepository import com.tistory.shanepark.dutypark.security.domain.dto.LoginMember import org.springframework.data.domain.Page -import org.springframework.data.domain.PageImpl import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import java.time.LocalDateTime -import java.time.YearMonth /** * When search engine is implemented, this service will be replaced with ScheduleSearchServiceESImpl @@ -20,7 +15,8 @@ import java.time.YearMonth @Service class ScheduleSearchServiceDBImpl( private val scheduleRepository: ScheduleRepository, - private val memberRepository: MemberRepository + private val memberRepository: MemberRepository, + private val friendService: FriendService, ) : ScheduleSearchService { override fun search( @@ -28,24 +24,16 @@ class ScheduleSearchServiceDBImpl( targetMemberId: Long, page: Pageable, keyword: String - ): Page { - // 1. get proper auth level for targetMemberId - - // 2. search schedules on database and sort by date desc - - // 3. return Page - - val member = memberRepository.findById(loginMember.id).orElseThrow() - val now = LocalDateTime.now() - val s1 = scheduleRepository.save(Schedule(member, "test1", now, now, 0, Visibility.FRIENDS)) - val s2 = scheduleRepository.save(Schedule(member, "test2", now, now, 0, Visibility.FRIENDS)) - val s3 = scheduleRepository.save(Schedule(member, "test3", now, now, 0, Visibility.FRIENDS)) - - val calendarView = CalendarView(YearMonth.now()) - val s1_ = ScheduleDto.of(calendarView, s1, false)[0] - val s2_ = ScheduleDto.of(calendarView, s2, false)[0] - val s3_ = ScheduleDto.of(calendarView, s3, false)[0] - return PageImpl(listOf(s3_, s2_, s1_), page, 3) + ): Page { + val target = memberRepository.findById(targetMemberId).orElseThrow() + val availableVisibilities = friendService.availableVisibilities(loginMember, target) + + return scheduleRepository.findByMemberAndContentContainingAndVisibilityIn( + member = target, + content = keyword, + visibility = availableVisibilities, + pageable = page + ).map { ScheduleSearchResult.of(it) } } } diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleService.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleService.kt index 8d9664a9..e5443685 100644 --- a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleService.kt +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleService.kt @@ -3,7 +3,6 @@ package com.tistory.shanepark.dutypark.schedule.service import com.tistory.shanepark.dutypark.common.domain.dto.CalendarView import com.tistory.shanepark.dutypark.common.exceptions.DutyparkAuthException import com.tistory.shanepark.dutypark.member.domain.entity.Member -import com.tistory.shanepark.dutypark.member.domain.enums.Visibility import com.tistory.shanepark.dutypark.member.repository.MemberRepository import com.tistory.shanepark.dutypark.member.service.FriendService import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleDto @@ -44,7 +43,7 @@ class ScheduleService( val start = calendarView.rangeFrom val end = calendarView.rangeEnd - val availableVisibilities = availableVisibilities(loginMember, member) + val availableVisibilities = friendService.availableVisibilities(loginMember, member) val userSchedules = scheduleRepository.findSchedulesOfMonth(member, start, end, visibilities = availableVisibilities) @@ -75,17 +74,6 @@ class ScheduleService( return array } - private fun availableVisibilities(loginMember: LoginMember?, member: Member): Collection { - if (loginMember == null) - return setOf(Visibility.PUBLIC) - if (loginMember.id == member.id) { - return Visibility.values().toList() - } - if (friendService.isFriend(loginMember.id, member.id!!)) { - return setOf(Visibility.PUBLIC, Visibility.FRIENDS) - } - return setOf(Visibility.PUBLIC) - } fun createSchedule(loginMember: LoginMember, scheduleUpdateDto: ScheduleUpdateDto): Schedule { val scheduleMember = memberRepository.findById(scheduleUpdateDto.memberId).orElseThrow() diff --git a/src/test/kotlin/com/tistory/shanepark/dutypark/DutyparkIntegrationTest.kt b/src/test/kotlin/com/tistory/shanepark/dutypark/DutyparkIntegrationTest.kt index 20b90233..8ffab3f4 100644 --- a/src/test/kotlin/com/tistory/shanepark/dutypark/DutyparkIntegrationTest.kt +++ b/src/test/kotlin/com/tistory/shanepark/dutypark/DutyparkIntegrationTest.kt @@ -8,8 +8,10 @@ import com.tistory.shanepark.dutypark.department.repository.DepartmentRepository import com.tistory.shanepark.dutypark.duty.domain.entity.DutyType import com.tistory.shanepark.dutypark.duty.repository.DutyTypeRepository import com.tistory.shanepark.dutypark.member.domain.dto.MemberCreateDto +import com.tistory.shanepark.dutypark.member.domain.entity.FriendRelation import com.tistory.shanepark.dutypark.member.domain.entity.Member import com.tistory.shanepark.dutypark.member.domain.enums.Visibility +import com.tistory.shanepark.dutypark.member.repository.FriendRelationRepository import com.tistory.shanepark.dutypark.member.repository.MemberRepository import com.tistory.shanepark.dutypark.member.service.MemberService import com.tistory.shanepark.dutypark.security.domain.dto.LoginMember @@ -36,6 +38,9 @@ class DutyparkIntegrationTest { @Autowired lateinit var dutyTypeRepository: DutyTypeRepository + @Autowired + lateinit var friendRelationRepository: FriendRelationRepository + @Autowired lateinit var em: EntityManager @@ -128,4 +133,9 @@ class DutyparkIntegrationTest { memberRepository.save(target) } + protected fun makeThemFriend(member1: Member, member2: Member) { + friendRelationRepository.save(FriendRelation(member1, member2)) + friendRelationRepository.save(FriendRelation(member2, member1)) + } + } diff --git a/src/test/kotlin/com/tistory/shanepark/dutypark/member/service/FriendServiceTest.kt b/src/test/kotlin/com/tistory/shanepark/dutypark/member/service/FriendServiceTest.kt index 088b0786..c2b3756a 100644 --- a/src/test/kotlin/com/tistory/shanepark/dutypark/member/service/FriendServiceTest.kt +++ b/src/test/kotlin/com/tistory/shanepark/dutypark/member/service/FriendServiceTest.kt @@ -20,9 +20,6 @@ class FriendServiceTest : DutyparkIntegrationTest() { @Autowired lateinit var friendService: FriendService - @Autowired - lateinit var friendRelationRepository: FriendRelationRepository - @Autowired lateinit var friendRequestRepository: FriendRequestRepository diff --git a/src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImplTest.kt b/src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImplTest.kt index 7103c4cb..c8bc903d 100644 --- a/src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImplTest.kt +++ b/src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImplTest.kt @@ -3,8 +3,9 @@ package com.tistory.shanepark.dutypark.schedule.service import com.tistory.shanepark.dutypark.DutyparkIntegrationTest import com.tistory.shanepark.dutypark.member.domain.enums.Visibility import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleUpdateDto +import com.tistory.shanepark.dutypark.schedule.domain.entity.Schedule import com.tistory.shanepark.dutypark.security.domain.dto.LoginMember -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.domain.Pageable @@ -32,18 +33,72 @@ class ScheduleSearchServiceDBImplTest : DutyparkIntegrationTest() { // When val result = scheduleSearchServiceDBImpl.search(loginMember, loginMember.id, Pageable.ofSize(10), "test") + System.err.println("result = ${result}") // Then - Assertions.assertThat(result).hasSize(3) - Assertions.assertThat(result.content.get(0).content).isEqualTo("test3") - Assertions.assertThat(result.content.get(1).content).isEqualTo("test2") - Assertions.assertThat(result.content.get(2).content).isEqualTo("test1") + assertThat(result).hasSize(3) + assertThat(result.content[0].content).isEqualTo("test3") + assertThat(result.content[1].content).isEqualTo("test2") + assertThat(result.content[2].content).isEqualTo("test1") + } + + @Test + fun `search schedule 20 results, it should be paginated`() { + val member = TestData.member + val loginMember = loginMember(member) + + // Given + for (i in 1..20) { + makeSchedule(loginMember, "test$i", LocalDateTime.of(2024, 1, 1, 0, i)) + } + + // When + val result = scheduleSearchServiceDBImpl.search(loginMember, loginMember.id, Pageable.ofSize(10), "test") + + // Then + assertThat(result).hasSize(10) + assertThat(result.content[0].content).isEqualTo("test20") + assertThat( + scheduleSearchServiceDBImpl.search( + loginMember, + loginMember.id, + Pageable.ofSize(10).next(), + "INVALID_KEYWORD" + ) + ).hasSize(0) + } + + @Test + fun `tagged schedules should be included in the search result`() { + // Given + val member1 = TestData.member + val loginMember = loginMember(member1) + val member2 = TestData.member2 + val loginMember2 = loginMember(member2) + makeThemFriend(member1, member2) + + makeSchedule(loginMember, "test1", LocalDateTime.of(2024, 1, 1, 0, 0)) + scheduleService.tagFriend( + loginMember = loginMember2, + scheduleId = makeSchedule( + loginMember2, "test-tagged", LocalDateTime.of(2024, 1, 1, 0, 1) + ).id, + friendId = member1.id!! + ) + + // When + val result = scheduleSearchServiceDBImpl.search(loginMember, loginMember.id, Pageable.ofSize(10), "test") + + // Then + assertThat(result).hasSize(2) + assertThat(result.content[0].content).isEqualTo("test-tagged") + assertThat(result.content[1].content).isEqualTo("test1") } private fun makeSchedule( loginMember: LoginMember, title: String, date: LocalDateTime - ) { - scheduleService.createSchedule( + ): Schedule { + return scheduleService.createSchedule( loginMember, ScheduleUpdateDto( loginMember.id, diff --git a/src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleServiceTest.kt b/src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleServiceTest.kt index c7b1c7e3..c3253c52 100644 --- a/src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleServiceTest.kt +++ b/src/test/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleServiceTest.kt @@ -3,10 +3,8 @@ package com.tistory.shanepark.dutypark.schedule.service import com.tistory.shanepark.dutypark.DutyparkIntegrationTest import com.tistory.shanepark.dutypark.common.domain.dto.CalendarView import com.tistory.shanepark.dutypark.common.exceptions.DutyparkAuthException -import com.tistory.shanepark.dutypark.member.domain.entity.FriendRelation import com.tistory.shanepark.dutypark.member.domain.entity.Member import com.tistory.shanepark.dutypark.member.domain.enums.Visibility -import com.tistory.shanepark.dutypark.member.repository.FriendRelationRepository import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleDto import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleUpdateDto import com.tistory.shanepark.dutypark.schedule.domain.entity.Schedule @@ -27,9 +25,6 @@ class ScheduleServiceTest : DutyparkIntegrationTest() { @Autowired lateinit var scheduleRepository: ScheduleRepository - @Autowired - lateinit var friendRelationRepository: FriendRelationRepository - @Test fun `Create schedule success test`() { // given @@ -802,11 +797,6 @@ class ScheduleServiceTest : DutyparkIntegrationTest() { } } - private fun makeThemFriend(member1: Member, member2: Member) { - friendRelationRepository.save(FriendRelation(member1, member2)) - friendRelationRepository.save(FriendRelation(member2, member1)) - } - @Test fun `if calendar visibility is public, even guest can get schedules`() { // Given From a8e4c9b82bea176a50d1def0bae3bc8f8aa4b17c Mon Sep 17 00:00:00 2001 From: shane Date: Mon, 13 Jan 2025 22:46:51 +0900 Subject: [PATCH 3/4] feat: search endpoint and search input TODO: - search UI ( idea: open an window under month navigator, keep `X` button on it so user can close it. make its design simple popup window --- .../dutypark/member/service/FriendService.kt | 2 +- .../schedule/controller/ScheduleController.kt | 16 +++++++++++++++ .../schedule/service/ScheduleSearchService.kt | 2 +- .../service/ScheduleSearchServiceDBImpl.kt | 4 ++-- src/main/resources/static/css/base.css | 8 ++++++++ src/main/resources/templates/duty/duty.html | 20 ++++++++++++------- 6 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/member/service/FriendService.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/member/service/FriendService.kt index 90f276dc..6a1819e0 100644 --- a/src/main/kotlin/com/tistory/shanepark/dutypark/member/service/FriendService.kt +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/member/service/FriendService.kt @@ -185,7 +185,7 @@ class FriendService( if (loginMember == null) return setOf(Visibility.PUBLIC) if (loginMember.id == member.id) { - return Visibility.values().toSet() + return Visibility.entries.toSet() } if (isFriend(loginMember.id, member.id!!)) { return setOf(Visibility.PUBLIC, Visibility.FRIENDS) diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/controller/ScheduleController.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/controller/ScheduleController.kt index e910d90d..e5664d32 100644 --- a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/controller/ScheduleController.kt +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/controller/ScheduleController.kt @@ -2,9 +2,14 @@ package com.tistory.shanepark.dutypark.schedule.controller import com.tistory.shanepark.dutypark.member.domain.annotation.Login import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleDto +import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleSearchResult import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleUpdateDto +import com.tistory.shanepark.dutypark.schedule.service.ScheduleSearchService import com.tistory.shanepark.dutypark.schedule.service.ScheduleService import com.tistory.shanepark.dutypark.security.domain.dto.LoginMember +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.web.PageableDefault import org.springframework.http.ResponseEntity import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.* @@ -15,6 +20,7 @@ import java.util.* @RequestMapping("/api/schedules") class ScheduleController( private val scheduleService: ScheduleService, + private val scheduleSearchService: ScheduleSearchService, ) { @GetMapping @@ -31,6 +37,16 @@ class ScheduleController( ) } + @GetMapping("/{id}/search") + fun searchSchedule( + @Login(required = false) loginMember: LoginMember, + @PathVariable(value = "id") targetMemberId: Long, + @PageableDefault(size = 10) pageable: Pageable, + @RequestParam q: String + ): Page { + return scheduleSearchService.search(loginMember, targetMemberId, pageable, q) + } + @PostMapping fun createSchedule( @RequestBody @Validated scheduleUpdateDto: ScheduleUpdateDto, diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt index f8e0f1c9..f00d4425 100644 --- a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchService.kt @@ -7,5 +7,5 @@ import org.springframework.data.domain.Pageable interface ScheduleSearchService { - fun search(loginMember: LoginMember, targetMemberId: Long, page: Pageable, keyword: String): Page + fun search(loginMember: LoginMember, targetMemberId: Long, page: Pageable, query: String): Page } diff --git a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt index 0665094e..3b5678ea 100644 --- a/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt +++ b/src/main/kotlin/com/tistory/shanepark/dutypark/schedule/service/ScheduleSearchServiceDBImpl.kt @@ -23,14 +23,14 @@ class ScheduleSearchServiceDBImpl( loginMember: LoginMember, targetMemberId: Long, page: Pageable, - keyword: String + query: String ): Page { val target = memberRepository.findById(targetMemberId).orElseThrow() val availableVisibilities = friendService.availableVisibilities(loginMember, target) return scheduleRepository.findByMemberAndContentContainingAndVisibilityIn( member = target, - content = keyword, + content = query, visibility = availableVisibilities, pageable = page ).map { ScheduleSearchResult.of(it) } diff --git a/src/main/resources/static/css/base.css b/src/main/resources/static/css/base.css index 4677654a..c38cb2a0 100644 --- a/src/main/resources/static/css/base.css +++ b/src/main/resources/static/css/base.css @@ -710,3 +710,11 @@ input[readonly], textarea[readonly] { background-color: #f8f9fa; cursor: not-allowed; } + +.search-bar input { + border-radius: 10px 0 0 10px; +} + +.search-bar .btn { + border-radius: 0 10px 10px 0; +} diff --git a/src/main/resources/templates/duty/duty.html b/src/main/resources/templates/duty/duty.html index c587ae7a..0258ae9c 100644 --- a/src/main/resources/templates/duty/duty.html +++ b/src/main/resources/templates/duty/duty.html @@ -25,9 +25,9 @@
-
+
- +
@@ -36,16 +36,22 @@

- +

-
-
- - +
+
From c71427cfcda79aeeb3dd069c70ab47c9dc8e3ead Mon Sep 17 00:00:00 2001 From: shane Date: Tue, 14 Jan 2025 22:23:53 +0900 Subject: [PATCH 4/4] feat : show search result TODO: when click search result, move to the page --- src/main/resources/templates/duty/duty.html | 80 +++++++++++++++++-- .../ScheduleSearchServiceDBImplTest.kt | 1 - 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/main/resources/templates/duty/duty.html b/src/main/resources/templates/duty/duty.html index 0258ae9c..de086dd7 100644 --- a/src/main/resources/templates/duty/duty.html +++ b/src/main/resources/templates/duty/duty.html @@ -44,9 +44,9 @@

+ +