From df25ca5a28e24fe1a3330236ca1a912a79acd38b Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 12:27:43 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`refacto?= =?UTF-8?q?r/IDLE-577`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @mjj111. * https://github.com/3IDLES/idle-server/pull/275#issuecomment-2907791900 The following files were modified: * `idle-application/src/main/kotlin/com/swm/idle/application/chat/domain/ChatMessageService.kt` * `idle-application/src/main/kotlin/com/swm/idle/application/chat/domain/ChatRoomService.kt` * `idle-application/src/main/kotlin/com/swm/idle/application/chat/facade/ChatFacadeService.kt` * `idle-application/src/main/kotlin/com/swm/idle/application/common/converter/PointConverter.kt` * `idle-application/src/main/kotlin/com/swm/idle/application/jobposting/domain/CrawlingJobPostingService.kt` * `idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CarerPostingFacadeService.kt` * `idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CenterPostingFacadeService.kt` * `idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CrawlingPostingFacadeService.kt` * `idle-application/src/main/kotlin/com/swm/idle/application/user/carer/domain/CarerService.kt` * `idle-application/src/main/kotlin/com/swm/idle/application/user/center/service/domain/CenterService.kt` * `idle-batch/src/main/kotlin/com/swm/idle/batch/common/launcher/CrawlingJobLauncher.kt` * `idle-batch/src/main/kotlin/com/swm/idle/batch/crawler/WorknetPageParser.kt` * `idle-batch/src/main/kotlin/com/swm/idle/batch/job/JobConfig.kt` * `idle-batch/src/main/kotlin/com/swm/idle/batch/step/PostingReader.kt` * `idle-batch/src/main/kotlin/com/swm/idle/batch/step/PostingWriter.kt` * `idle-domain/src/main/kotlin/com/swm/idle/domain/chat/event/ChatRedisTemplate.kt` * `idle-domain/src/main/kotlin/com/swm/idle/domain/chat/repository/ChatMessageRepository.kt` * `idle-domain/src/main/kotlin/com/swm/idle/domain/chat/repository/ChatRoomRepository.kt` * `idle-domain/src/main/kotlin/com/swm/idle/domain/chat/vo/ChatRoomSummaryInfo.kt` * `idle-domain/src/main/kotlin/com/swm/idle/domain/chat/vo/ChatRoomSummaryInfoProjection.kt` * `idle-domain/src/main/kotlin/com/swm/idle/domain/jobposting/repository/redis/RedisJobPostingRepository.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/batch/api/BatchApi.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/batch/controller/BatchController.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/api/ChatCarerApi.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/api/ChatCenterApi.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatCarerController.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatCenterController.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatSocketController.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/handler/ChatHandler.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/api/CrawlingJobPostingApi.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CarerJobPostingController.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CenterJobPostingController.kt` * `idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CrawlingJobPostingController.kt` * `idle-support/transfer/src/main/kotlin/com/swm/idle/support/transfer/jobposting/carer/CrawlingJobPostingScrollResponse.kt` * `idle-support/transfer/src/main/kotlin/com/swm/idle/support/transfer/jobposting/common/CrawlingJobPostingResponse.kt` --- .../chat/domain/ChatMessageService.kt | 15 ++++ .../chat/domain/ChatRoomService.kt | 27 +++++++ .../chat/facade/ChatFacadeService.kt | 74 +++++++++++++++++++ .../common/converter/PointConverter.kt | 17 +++++ .../domain/CrawlingJobPostingService.kt | 25 +++++++ .../facade/CarerPostingFacadeService.kt | 45 +++++++++++ .../facade/CenterPostingFacadeService.kt | 32 ++++++++ .../facade/CrawlingPostingFacadeService.kt | 21 ++++++ .../user/carer/domain/CarerService.kt | 12 +++ .../center/service/domain/CenterService.kt | 13 ++++ .../common/launcher/CrawlingJobLauncher.kt | 8 ++ .../idle/batch/crawler/WorknetPageParser.kt | 13 ++++ .../com/swm/idle/batch/job/JobConfig.kt | 36 +++++++++ .../com/swm/idle/batch/step/PostingReader.kt | 7 ++ .../com/swm/idle/batch/step/PostingWriter.kt | 15 ++++ .../domain/chat/event/ChatRedisTemplate.kt | 50 +++++++++++++ .../chat/repository/ChatMessageRepository.kt | 9 ++- .../chat/repository/ChatRoomRepository.kt | 15 +++- .../domain/chat/vo/ChatRoomSummaryInfo.kt | 6 ++ .../chat/vo/ChatRoomSummaryInfoProjection.kt | 40 ++++++++-- .../redis/RedisJobPostingRepository.kt | 37 ++++++++++ .../idle/presentation/batch/api/BatchApi.kt | 5 ++ .../batch/controller/BatchController.kt | 7 +- .../presentation/chat/api/ChatCarerApi.kt | 9 ++- .../presentation/chat/api/ChatCenterApi.kt | 9 ++- .../chat/controller/ChatCarerController.kt | 12 +++ .../chat/controller/ChatCenterController.kt | 12 +++ .../chat/controller/ChatSocketController.kt | 20 +++++ .../presentation/chat/handler/ChatHandler.kt | 10 +++ .../jobposting/api/CrawlingJobPostingApi.kt | 6 ++ .../controller/CarerJobPostingController.kt | 36 +++++++++ .../controller/CenterJobPostingController.kt | 16 ++++ .../CrawlingJobPostingController.kt | 17 +++++ .../carer/CrawlingJobPostingScrollResponse.kt | 9 +++ .../common/CrawlingJobPostingResponse.kt | 9 +++ 35 files changed, 683 insertions(+), 11 deletions(-) diff --git a/idle-application/src/main/kotlin/com/swm/idle/application/chat/domain/ChatMessageService.kt b/idle-application/src/main/kotlin/com/swm/idle/application/chat/domain/ChatMessageService.kt index 725864c8..e7f4bb20 100644 --- a/idle-application/src/main/kotlin/com/swm/idle/application/chat/domain/ChatMessageService.kt +++ b/idle-application/src/main/kotlin/com/swm/idle/application/chat/domain/ChatMessageService.kt @@ -11,6 +11,14 @@ import java.util.* class ChatMessageService ( private val chatMessageRepository: ChatMessageRepository ){ + /** + * Creates and persists a new chat message using the provided request data, sender ID, and sequence number. + * + * @param request The request containing chat room ID, receiver ID, and message content. + * @param userId The UUID of the user sending the message. + * @param sequence The sequence number to assign to the message. + * @return The saved ChatMessage entity. + */ @Transactional fun save(request: SendChatMessageRequest, userId: UUID, sequence: Long): ChatMessage { val message = ChatMessage( @@ -23,6 +31,13 @@ class ChatMessageService ( return chatMessageRepository.save(message) } + /** + * Retrieves recent chat messages in a chat room after a specified message. + * + * @param chatroomId The unique identifier of the chat room. + * @param messageId The unique identifier of the message after which to retrieve messages. + * @return A list of recent chat messages following the specified message in the chat room. + */ @Transactional fun getRecentMessages(chatroomId: UUID, messageId: UUID): List { return chatMessageRepository.getRecentMessages(chatroomId, messageId) diff --git a/idle-application/src/main/kotlin/com/swm/idle/application/chat/domain/ChatRoomService.kt b/idle-application/src/main/kotlin/com/swm/idle/application/chat/domain/ChatRoomService.kt index 02c68291..34904ecf 100644 --- a/idle-application/src/main/kotlin/com/swm/idle/application/chat/domain/ChatRoomService.kt +++ b/idle-application/src/main/kotlin/com/swm/idle/application/chat/domain/ChatRoomService.kt @@ -12,16 +12,43 @@ class ChatRoomService( private val chatRoomRepository: ChatRoomRepository ){ + /** + * Creates a chat room for the given carer and center IDs if one does not already exist. + * + * If a chat room already exists for the specified carer and center, returns its ID; otherwise, creates a new chat room and returns its ID. + * + * @param carerId The UUID of the carer. + * @param centerId The UUID of the center. + * @return The UUID of the existing or newly created chat room. + */ fun create(carerId: UUID, centerId: UUID): UUID { val existing = chatroomRepository.findByCarerIdAndCenterId(carerId, centerId) return existing?.id ?: chatroomRepository.save(ChatRoom(carerId = carerId, centerId = centerId)).id } + /** + * Retrieves a chat room by its unique identifier. + * + * @param chatRoomId The UUID of the chat room to retrieve. + * @return The corresponding ChatRoom entity. + * @throws NoSuchElementException if no chat room with the given ID exists. + */ fun getById(chatRoomId: UUID): ChatRoom { return chatroomRepository.findById(chatRoomId) .orElseThrow() } + /** + * Retrieves summaries of chat rooms with their latest messages for the given room IDs. + * + * Converts the provided set of string room IDs to UUIDs, fetches chat rooms and their last messages, + * and constructs summary information for each. The opponent ID in each summary is determined by the `isCarer` flag. + * + * @param roomIds Set of chat room IDs as strings. + * @param isCarer Indicates whether the requesting user is a carer (true) or a center (false). + * @return List of chat room summaries, each containing the chat room ID, opponent ID, last message content, + * last message timestamp, and last message sequence number. + */ fun findChatRoomsWithLastMessages(roomIds: Set, isCarer: Boolean): List { val uuidSet = roomIds.map(UUID::fromString).toSet() val projections = chatRoomRepository.findChatRoomsWithLastMessages(uuidSet) diff --git a/idle-application/src/main/kotlin/com/swm/idle/application/chat/facade/ChatFacadeService.kt b/idle-application/src/main/kotlin/com/swm/idle/application/chat/facade/ChatFacadeService.kt index 4b368f65..b073b85b 100644 --- a/idle-application/src/main/kotlin/com/swm/idle/application/chat/facade/ChatFacadeService.kt +++ b/idle-application/src/main/kotlin/com/swm/idle/application/chat/facade/ChatFacadeService.kt @@ -36,6 +36,15 @@ class ChatFacadeService( private val carerService: CarerService, ) { + /** + * Sends a chat message from either a carer or a center user. + * + * Determines the sender's user ID based on the sender type, saves the message with a sequence number, updates unread chat room status, publishes the message, and triggers notification delivery to the recipient. + * + * @param request The chat message request containing message and recipient details. + * @param inputId The UUID of the sender (carer or center manager). + * @param isCarer Indicates whether the sender is a carer (`true`) or a center user (`false`). + */ @Transactional fun send(request: SendChatMessageRequest, inputId: UUID, isCarer: Boolean) { val userId = if(isCarer) inputId else getCenterId(inputId) @@ -49,6 +58,15 @@ class ChatFacadeService( sendNotification(message, request, isCarer) } + /** + * Sends a push notification for a chat message to the appropriate recipient(s) if they are not actively chatting. + * + * If the sender is a carer, notifications are sent to all center managers of the receiving center who are not currently in the chat. If the sender is a center, a notification is sent to the receiving carer if they are not currently in the chat. No notification is sent if the recipient is actively chatting or if no device token is found. + * + * @param message The chat message to notify about. + * @param request The original chat message request containing sender and receiver information. + * @param isCarer Indicates whether the sender is a carer. + */ private fun sendNotification( message: ChatMessage, request: SendChatMessageRequest, @@ -68,12 +86,28 @@ class ChatFacadeService( } } + /** + * Retrieves all center managers associated with the specified center ID. + * + * @param centerId The unique identifier of the center. + * @return A list of center managers for the given center, or an empty list if none are found. + */ private fun getManagersByCenterId(centerId:UUID): List { val businessNumber = BusinessRegistrationNumber(centerService.getById(centerId).businessRegistrationNumber) val centerManagers = centerManagerService.findAllByCenterBusinessRegistrationNumber(businessNumber)?: emptyList() return centerManagers } + /** + * Marks chat messages as read for a user in a chat room and updates the read sequence in Redis. + * + * Removes the chat room from the user's unread list, updates the user's read sequence for the chat room, + * and publishes a read event to notify other participants. + * + * @param request The request containing chat room ID, opponent ID, and the sequence number up to which messages are read. + * @param inputId The UUID of the acting user (carer or center manager). + * @param isCarer Indicates whether the acting user is a carer (`true`) or a center manager (`false`). + */ @Transactional fun read(request: ReadChatMessagesReqeust, inputId: UUID, isCarer: Boolean) { val userId = if(isCarer) inputId else getCenterId(inputId) @@ -89,6 +123,13 @@ class ChatFacadeService( chatRedisTemplate.publish(readMessage) } + /** + * Retrieves the center ID associated with the given center manager ID. + * + * @param managerId The UUID of the center manager. + * @return The UUID of the center managed by the specified manager. + * @throws CenterException.NotFoundException if no center is found for the manager's business registration number. + */ private fun getCenterId(managerId:UUID): UUID { val manager = centerManagerService.getById(managerId) val businessNumber = BusinessRegistrationNumber(manager.centerBusinessRegistrationNumber) @@ -96,6 +137,15 @@ class ChatFacadeService( return center.id } + /** + * Creates a new chat room between a carer and a center based on the sender's role. + * + * Determines the carer and center IDs according to the sender's authentication and the provided opponent ID, then creates the chat room and returns its ID. + * + * @param request The request containing the opponent's ID. + * @param isCarer Indicates whether the sender is a carer. + * @return The response containing the created chat room's ID. + */ @Transactional fun createChatroom(request: CreateChatRoomRequest, isCarer: Boolean):CreateChatRoomResponse { val (carerId, centerId) = @@ -110,6 +160,12 @@ class ChatFacadeService( return CreateChatRoomResponse(chatRoomId) } + /** + * Retrieves the center ID associated with the currently authenticated center manager. + * + * @return The UUID of the center linked to the authenticated manager. + * @throws CenterException.NotFoundException if no center is found for the manager's business registration number. + */ fun getCenterIdByAuthentication():UUID { val managerId = getUserAuthentication().userId val manager = centerManagerService.getById(managerId) @@ -118,6 +174,16 @@ class ChatFacadeService( return center.id } + /** + * Retrieves recent chat messages for a chat room along with the opponent's read sequence. + * + * If `messageId` is null, a new UUID is generated to fetch messages. The opponent's ID is determined based on the sender's role, and their read sequence is retrieved from Redis. + * + * @param chatRoomId The ID of the chat room to fetch messages from. + * @param messageId The message ID to start fetching from; if null, a new UUID is used. + * @param isCarer Indicates whether the requester is a carer. + * @return A response containing the list of recent chat messages and the opponent's read sequence. + */ @Transactional(readOnly = true) fun getRecentMessages( chatRoomId: UUID, @@ -135,6 +201,14 @@ class ChatFacadeService( return ChatMessageResponse(messageInfo, opponentReadSequence) } + /** + * Retrieves a summary of chat rooms for the authenticated user, including unread message counts and opponent details. + * + * For each chat room, the summary includes the last message, adjusted unread message count based on read sequences, and opponent's name and profile image. Opponent information is determined by the user's role (carer or center). + * + * @param isCarer Indicates whether the authenticated user is a carer. + * @return A list of chat room summaries enriched with opponent information and unread message counts. + */ @Transactional(readOnly = true) fun getChatroomSummary(isCarer: Boolean): List { val userId = if (isCarer) getUserAuthentication().userId else getCenterIdByAuthentication() diff --git a/idle-application/src/main/kotlin/com/swm/idle/application/common/converter/PointConverter.kt b/idle-application/src/main/kotlin/com/swm/idle/application/common/converter/PointConverter.kt index 84caacce..53b6d714 100644 --- a/idle-application/src/main/kotlin/com/swm/idle/application/common/converter/PointConverter.kt +++ b/idle-application/src/main/kotlin/com/swm/idle/application/common/converter/PointConverter.kt @@ -15,6 +15,14 @@ object PointConverter { SPATIAL_REFERENCE_IDENTIFIER_NUMBER ) + /** + * Converts the latitude and longitude of a Carer entity into a JTS Point. + * + * The resulting Point uses the longitude as the x-coordinate and latitude as the y-coordinate. + * + * @param carer The Carer entity whose geographic coordinates are to be converted. + * @return A Point representing the Carer's location. + */ fun convertToPoint(carer: Carer): Point { val latitude = carer.latitude.toDouble() val longitude = carer.longitude.toDouble() @@ -22,6 +30,15 @@ object PointConverter { return geometryFactory.createPoint(Coordinate(longitude, latitude)) } + /** + * Converts latitude and longitude coordinates to a JTS `Point` object. + * + * The longitude is used as the x-coordinate and the latitude as the y-coordinate. + * + * @param latitude The latitude value. + * @param longitude The longitude value. + * @return A `Point` representing the specified geographic location. + */ fun convertToPoint(latitude: Double, longitude: Double ): Point { return geometryFactory.createPoint(Coordinate(longitude, latitude)) } diff --git a/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/domain/CrawlingJobPostingService.kt b/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/domain/CrawlingJobPostingService.kt index 09972e7d..7966a6a8 100644 --- a/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/domain/CrawlingJobPostingService.kt +++ b/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/domain/CrawlingJobPostingService.kt @@ -18,11 +18,29 @@ class CrawlingJobPostingService( private val redisJobPostingRepository: RedisJobPostingRepository, ) { + /** + * Retrieves a crawled job posting by its unique identifier. + * + * @param crawlingJobPostingId The UUID of the job posting to retrieve. + * @return The corresponding CrawledJobPosting entity. + * @throws PersistenceException.ResourceNotFound if the job posting does not exist. + */ fun getById(crawlingJobPostingId: UUID): CrawledJobPosting { return crawlingJobPostingJpaRepository.findByIdOrNull(crawlingJobPostingId) ?: throw PersistenceException.ResourceNotFound("크롤링한 구인 공고(id=$crawlingJobPostingId)를 찾을 수 없습니다") } + /** + * Retrieves a list of job posting previews within a specified distance from a given location. + * + * Queries Redis for job posting IDs near the provided location within the given distance, fetches the corresponding postings from the database, and returns preview DTOs for each posting. + * + * @param next Optional UUID for pagination; results start after this posting if provided. + * @param location The geographic point from which to search. + * @param distance The maximum distance from the location, in meters. + * @param limit The maximum number of results to return. + * @return A list of job posting preview DTOs within the specified range. + */ fun findAllInRange( next: UUID?, location: Point, @@ -35,6 +53,13 @@ class CrawlingJobPostingService( } + /** + * Calculates the distance in meters between a job posting's location and a specified carer's location. + * + * @param crawledJobPosting The job posting whose location is used as the starting point. + * @param carerLocation The geographic location to measure the distance to. + * @return The distance in meters as an integer. + */ fun calculateDistance( crawledJobPosting: CrawledJobPosting, carerLocation: Point, diff --git a/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CarerPostingFacadeService.kt b/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CarerPostingFacadeService.kt index 23fd818d..5549e723 100644 --- a/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CarerPostingFacadeService.kt +++ b/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CarerPostingFacadeService.kt @@ -33,6 +33,14 @@ class CarerPostingFacadeService( private val jobPostingFavoriteService: JobPostingFavoriteService, ) { + /** + * Retrieves detailed information about a specific job posting for the authenticated carer. + * + * Assembles job posting details including associated weekdays, life assistance types, apply methods, center information, distance from the carer, application status, and favorite status. + * + * @param jobPostingId The unique identifier of the job posting to retrieve. + * @return A response containing comprehensive job posting details tailored to the authenticated carer. + */ fun getJobPostingDetail(jobPostingId: UUID): CarerJobPostingResponse { val carer = carerService.getById(getUserAuthentication().userId) val posting = jobPostingService.getById(jobPostingId) @@ -72,6 +80,14 @@ class CarerPostingFacadeService( ) } + /** + * Retrieves a paginated list of job postings within range of the authenticated carer's location. + * + * Uses the carer's current location to find nearby job postings and returns them in a scrollable response format. + * + * @param request Cursor-based pagination request containing the next cursor and limit. + * @return A scroll response containing job postings near the carer, the next cursor, and the total count in the current page. + */ fun getJobPostingsInRange( request: CursorScrollRequest, ): CarerJobPostingScrollResponse { @@ -91,6 +107,16 @@ class CarerPostingFacadeService( ) } + /** + * Retrieves a paginated list of job posting previews within range of the specified location for the authenticated carer. + * + * Calculates the distance from each job posting to the carer's location and determines the next pagination cursor. + * + * @param location The geographic point to search from. + * @param next The pagination cursor indicating the starting point for the next page, or null to start from the beginning. + * @param limit The maximum number of job postings to return. + * @return A pair containing the list of job posting previews (with distances set) and the next cursor UUID, or null if there are no more results. + */ private fun scrollByCarerLocationInRange( location: Point, next: UUID?, @@ -126,6 +152,12 @@ class CarerPostingFacadeService( return items to newNext } + /** + * Retrieves a paginated list of job postings that the authenticated carer has applied to. + * + * @param request The cursor-based pagination request. + * @return A scroll response containing applied job postings, the next pagination cursor, and the total count. + */ fun getAppliedJobPostings(request: CursorScrollRequest): CarerAppliedJobPostingScrollResponse { val (items, next) = scrollByCarerApplyHistory( next = request.next, @@ -140,6 +172,14 @@ class CarerPostingFacadeService( ) } + /** + * Retrieves a paginated list of job postings the specified carer has applied to, including distance from the carer's current location. + * + * @param carerId The unique identifier of the carer whose application history is being queried. + * @param next The pagination cursor indicating the starting point for the next page, or null to start from the beginning. + * @param limit The maximum number of job postings to return. + * @return A pair containing the list of job posting previews (with distance calculated) and the next pagination cursor, or null if there are no more results. + */ private fun scrollByCarerApplyHistory( carerId: UUID, next: UUID?, @@ -172,6 +212,11 @@ class CarerPostingFacadeService( return items to newNext } + /** + * Retrieves all job postings favorited by the authenticated carer, including the distance from the carer's current location. + * + * @return A response containing the list of favorite job postings with distance information. + */ fun getMyFavoriteJobPostings(): JobPostingFavoriteResponse { val carer = carerService.getById(getUserAuthentication().userId) val location = PointConverter.convertToPoint(carer) diff --git a/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CenterPostingFacadeService.kt b/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CenterPostingFacadeService.kt index 8a02a3ad..bda28905 100644 --- a/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CenterPostingFacadeService.kt +++ b/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CenterPostingFacadeService.kt @@ -54,6 +54,15 @@ class CenterPostingFacadeService( private val createJobPostingEventPublisher: CreateJobPostingEventPublisher, ) { + /** + * Creates a new job posting for the authenticated center, including related entities and notifications. + * + * Retrieves the center ID for the authenticated user, geocodes the provided address, and creates a job posting with location data. + * Concurrently creates associated life assistance, weekdays, and apply methods if provided. + * Notifies all carers within the job posting's location radius by sending notifications to their registered devices. + * + * @param request The details required to create the job posting, including address, life assistance, weekdays, and apply methods. + */ @Transactional suspend fun create(request: CreateJobPostingRequest) { val centerId = getCenterId() @@ -121,6 +130,12 @@ class CenterPostingFacadeService( } } + /** + * Retrieves the UUID of the center associated with the currently authenticated center manager. + * + * @return The UUID of the center. + * @throws CenterException.NotFoundException if the center cannot be found for the authenticated user. + */ private fun getCenterId(): UUID { val centerId = centerManagerService.getById(getUserAuthentication().userId).let { @@ -131,6 +146,14 @@ class CenterPostingFacadeService( return centerId } + /** + * Constructs a notification message summarizing key details of a job posting. + * + * The message includes the first three segments of the lot number address, care level, calculated age from birth year, and gender. + * + * @param jobPosting The job posting from which to extract details. + * @return A formatted message string for notifications. + */ private fun createBodyMessage(jobPosting: JobPosting): String { val filteredLotNumberAddress = jobPosting.lotNumberAddress.split(" ") .take(3) @@ -142,6 +165,15 @@ class CenterPostingFacadeService( jobPosting.gender.value } + /** + * Updates an existing job posting with new details, including optional address and related entities. + * + * If both road name and lot number addresses are provided, updates the job posting's location using geocoding. + * Also updates associated life assistance, weekdays, and apply methods if present in the request. + * + * @param postingId The unique identifier of the job posting to update. + * @param request The update request containing new job posting details. + */ @Transactional fun update( postingId: UUID, diff --git a/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CrawlingPostingFacadeService.kt b/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CrawlingPostingFacadeService.kt index c0d088b7..7b2378eb 100644 --- a/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CrawlingPostingFacadeService.kt +++ b/idle-application/src/main/kotlin/com/swm/idle/application/jobposting/facade/CrawlingPostingFacadeService.kt @@ -22,6 +22,14 @@ class CrawlingPostingFacadeService( private val carerService: CarerService, private val jobPostingFavoriteService: JobPostingFavoriteService, ) { + /** + * Retrieves a paginated list of job postings within a specified distance from the authenticated carer's location. + * + * The search expands the distance incrementally until the requested number of postings is found, the maximum distance (30 units) is reached, or three consecutive searches yield no results. Results are accumulated using cursor-based pagination. + * + * @param request The scroll request containing pagination and distance parameters. + * @return A scroll response with the collected job postings and the final search distance used. + */ fun getCrawlingPostingsInRange(request: CrawlingCursorScrollRequest): CrawlingJobPostingScrollResponse { val carer = carerService.getById(getUserAuthentication().userId) val location = PointConverter.convertToPoint(carer) @@ -55,6 +63,12 @@ class CrawlingPostingFacadeService( return CrawlingJobPostingScrollResponse.from(result, distance) } + /** + * Retrieves detailed information about a specific job posting, including its distance from the authenticated carer and favorite status. + * + * @param postingId The unique identifier of the job posting to retrieve. + * @return A response containing the job posting details, the distance from the carer, and whether it is marked as a favorite. + */ fun getCrawlingJobPosting(postingId: UUID): CrawlingJobPostingResponse { val carer = carerService.getById(getUserAuthentication().userId) val posting = crawlingJobPostingService.getById(postingId) @@ -66,6 +80,13 @@ class CrawlingPostingFacadeService( return CrawlingJobPostingResponse.from(posting, isFavorite, distance) } + /** + * Retrieves the authenticated carer's favorite job postings with distance information. + * + * Returns a response containing a list of the carer's favorite job postings, each including the distance from the carer's current location. + * + * @return A response object with favorite job postings and their respective distances. + */ fun getFavoriteCrawlingJobPostings(): CrawlingJobPostingFavoriteResponse { val carer = carerService.getById(getUserAuthentication().userId) val carerPoint = PointConverter.convertToPoint(carer) diff --git a/idle-application/src/main/kotlin/com/swm/idle/application/user/carer/domain/CarerService.kt b/idle-application/src/main/kotlin/com/swm/idle/application/user/carer/domain/CarerService.kt index 06d4df19..933139cb 100644 --- a/idle-application/src/main/kotlin/com/swm/idle/application/user/carer/domain/CarerService.kt +++ b/idle-application/src/main/kotlin/com/swm/idle/application/user/carer/domain/CarerService.kt @@ -107,10 +107,22 @@ class CarerService( carerJpaRepository.deleteById(id) } + /** + * Retrieves all carers located within a certain radius of the specified geographic point. + * + * @param location The geographic point to search around. + * @return A list of carers within the radius, or null if none are found. + */ fun findAllByLocationWithinRadius(location: Point): List? { return carerQueryRepository.findAllByLocationWithinRadius(location) } + /** + * Retrieves a list of Carer entities matching the given set of IDs. + * + * @param carerIds Set of UUIDs representing the Carer entities to retrieve. + * @return List of Carer entities corresponding to the provided IDs. If no matches are found, returns an empty list. + */ fun getByIds(carerIds: Set): List { return carerJpaRepository.findAllById(carerIds) } diff --git a/idle-application/src/main/kotlin/com/swm/idle/application/user/center/service/domain/CenterService.kt b/idle-application/src/main/kotlin/com/swm/idle/application/user/center/service/domain/CenterService.kt index 4ab3be34..755f8a49 100644 --- a/idle-application/src/main/kotlin/com/swm/idle/application/user/center/service/domain/CenterService.kt +++ b/idle-application/src/main/kotlin/com/swm/idle/application/user/center/service/domain/CenterService.kt @@ -61,11 +61,24 @@ class CenterService( ) } + /** + * Retrieves a Center entity by its unique identifier. + * + * @param centerId The UUID of the Center to retrieve. + * @return The Center entity with the specified ID. + * @throws PersistenceException.ResourceNotFound if no Center with the given ID exists. + */ fun getById(centerId: UUID): Center { return centerJpaRepository.findByIdOrNull(centerId) ?: throw PersistenceException.ResourceNotFound("Center(id=$centerId)를 찾을 수 없습니다") } + /** + * Retrieves a list of Center entities matching the provided set of center IDs. + * + * @param centerIds Set of UUIDs representing the IDs of the centers to retrieve. + * @return List of Center entities corresponding to the given IDs. If no centers are found for some IDs, those are omitted from the result. + */ fun getByIds(centerIds : Set) : List
{ return centerJpaRepository.findAllById(centerIds) } diff --git a/idle-batch/src/main/kotlin/com/swm/idle/batch/common/launcher/CrawlingJobLauncher.kt b/idle-batch/src/main/kotlin/com/swm/idle/batch/common/launcher/CrawlingJobLauncher.kt index fdee5e31..2eae959e 100644 --- a/idle-batch/src/main/kotlin/com/swm/idle/batch/common/launcher/CrawlingJobLauncher.kt +++ b/idle-batch/src/main/kotlin/com/swm/idle/batch/common/launcher/CrawlingJobLauncher.kt @@ -14,6 +14,9 @@ class CrawlingJobLauncher( private val jobRegistry: JobRegistry, ) { + /** + * Schedules and launches the "crawlingJob" batch job daily at 22:00 with the current timestamp and a day offset of 0. + */ @Scheduled(cron = "0 0 22 * * *") fun scheduleJob() { val jobParameters: JobParameters = JobParametersBuilder() @@ -25,6 +28,11 @@ class CrawlingJobLauncher( } + /** + * Manually triggers the "crawlingJob" batch job with a specified day offset. + * + * @param dayOffset The offset in days to include as a job parameter. Defaults to 0. + */ fun jobStart(dayOffset: Long = 0) { val jobParameters: JobParameters = JobParametersBuilder() .addLong("timestamp", System.currentTimeMillis()) diff --git a/idle-batch/src/main/kotlin/com/swm/idle/batch/crawler/WorknetPageParser.kt b/idle-batch/src/main/kotlin/com/swm/idle/batch/crawler/WorknetPageParser.kt index 9d6a684c..06dd72fe 100644 --- a/idle-batch/src/main/kotlin/com/swm/idle/batch/crawler/WorknetPageParser.kt +++ b/idle-batch/src/main/kotlin/com/swm/idle/batch/crawler/WorknetPageParser.kt @@ -39,12 +39,25 @@ class WorknetPageParser(dayOffset: Long = 0) { return CrawlerConsts.JOB_POSTING_COUNT_PER_PAGE.getIntValue() } + /** + * Generates the URL for accessing a specific page of job postings. + * + * Replaces the page index parameter in the base crawling URL with the given page number. + * + * @param currentPage The page number to access. + * @return The URL for the specified page. + */ fun getAccessURL(currentPage: Int): String { return crawlingUrl.replace( Regex("pageIndex=\\d+"), "pageIndex=$currentPage") } + /** + * Constructs the crawling URL by substituting the target date, offset by the specified number of days, and setting the page index to 1. + * + * @param dayOffset The number of days to subtract from the current date to determine the target date for the URL. + */ private fun getCrawlingURL(dayOffset: Long) { val targetDate = LocalDate.now().minusDays(dayOffset) crawlingUrl = CrawlerConsts.CRAWLING_TARGET_URL_FORMAT.value diff --git a/idle-batch/src/main/kotlin/com/swm/idle/batch/job/JobConfig.kt b/idle-batch/src/main/kotlin/com/swm/idle/batch/job/JobConfig.kt index 11c32a65..74bb12fe 100644 --- a/idle-batch/src/main/kotlin/com/swm/idle/batch/job/JobConfig.kt +++ b/idle-batch/src/main/kotlin/com/swm/idle/batch/job/JobConfig.kt @@ -32,6 +32,12 @@ class JobConfig( private val redisTemplate: RedisTemplate ) { + /** + * Defines the "crawlingJob" batch job, starting with the provided step and preventing restarts after completion. + * + * @param crawlStep The initial step to execute in the job. + * @return The configured batch job instance. + */ @Bean fun crawlingJob(crawlStep: Step): Job { return JobBuilder("crawlingJob", jobRepository) @@ -40,6 +46,14 @@ class JobConfig( .build() } + /** + * Defines the batch step for crawling job postings, configuring chunk-oriented processing with parallel execution. + * + * The step reads lists of `CrawledJobPostingDto`, processes them into lists of `CrawledJobPosting`, and writes the results. + * Chunk size is set to 1, and the step allows parallel execution with a concurrency limit. The step can be restarted even if previously completed. + * + * @return The configured batch step for crawling job postings. + */ @Bean fun crawlStep( postingReader: ItemReader>, @@ -55,6 +69,11 @@ class JobConfig( .build() } + /** + * Creates a SimpleAsyncTaskExecutor with a concurrency limit of 4 for parallel task execution. + * + * @return A SimpleAsyncTaskExecutor configured to allow up to 4 concurrent tasks. + */ @Bean fun taskExecutor(): SimpleAsyncTaskExecutor { return SimpleAsyncTaskExecutor().apply { @@ -62,6 +81,12 @@ class JobConfig( } } + /** + * Creates a step-scoped `ItemReader` that reads lists of `CrawledJobPostingDto` based on the provided "day" job parameter. + * + * @param dayParam The "day" job parameter used to determine which postings to read; defaults to 0 if not provided. + * @return An `ItemReader` that supplies lists of crawled job posting DTOs for the batch step. + */ @Bean @StepScope fun postingReader( @@ -71,11 +96,22 @@ class JobConfig( return PostingReader(day) } + /** + * Provides an item processor that converts lists of `CrawledJobPostingDto` objects into lists of `CrawledJobPosting` entities. + * + * @return An `ItemProcessor` for transforming DTOs to entities in batch processing. + */ @Bean fun postingProcessor(): ItemProcessor, out List> { return PostingProcessor() } + /** + * Creates a step-scoped writer for persisting lists of crawled job postings. + * + * @param dayParam The job parameter indicating the day offset for processing; defaults to 0 if not provided. + * @return An ItemWriter that writes lists of CrawledJobPosting entities using the configured entity manager and Redis template. + */ @Bean @StepScope fun postingWriter( diff --git a/idle-batch/src/main/kotlin/com/swm/idle/batch/step/PostingReader.kt b/idle-batch/src/main/kotlin/com/swm/idle/batch/step/PostingReader.kt index 8692282c..06479297 100644 --- a/idle-batch/src/main/kotlin/com/swm/idle/batch/step/PostingReader.kt +++ b/idle-batch/src/main/kotlin/com/swm/idle/batch/step/PostingReader.kt @@ -16,6 +16,13 @@ class PostingReader( var pageIndex = AtomicInteger(1) } + /** + * Retrieves the next page of crawled job postings as a list. + * + * Returns `null` if all available pages have been read, signaling the end of the data stream. + * + * @return A list of `CrawledJobPostingDto` objects for the current page, or `null` if no more pages are available. + */ override fun read(): List? { val accessIndex = pageIndex.getAndIncrement() diff --git a/idle-batch/src/main/kotlin/com/swm/idle/batch/step/PostingWriter.kt b/idle-batch/src/main/kotlin/com/swm/idle/batch/step/PostingWriter.kt index bd022d05..ad31184e 100644 --- a/idle-batch/src/main/kotlin/com/swm/idle/batch/step/PostingWriter.kt +++ b/idle-batch/src/main/kotlin/com/swm/idle/batch/step/PostingWriter.kt @@ -21,6 +21,14 @@ class PostingWriter( setEntityManagerFactory(entityManagerFactory) } + /** + * Persists a chunk of job postings to the database and updates their geospatial data in Redis. + * + * Flattens the provided chunk of job posting lists, writes all postings to the database using JPA, + * and then stores each posting's geolocation in Redis with an expiration based on the configured offset. + * + * @param chunk A chunk containing lists of `CrawledJobPosting` entities to be processed. + */ override fun write(chunk: Chunk>) { val flatChunk = Chunk() chunk.forEach { itemList -> @@ -34,6 +42,13 @@ class PostingWriter( } } + /** + * Stores the geolocation data of a list of job postings in Redis under a date-based geo-indexed key. + * + * Each posting's coordinates and ID are added to a Redis geo set for the current date. The Redis key is set to expire after a duration calculated as 13 days minus the configured day offset. + * + * @param itemList The list of job postings whose geolocation data will be stored in Redis. + */ private fun saveToRedis(itemList: List) { val today = LocalDate.now() val redisKey = "job_postings_geo_${today.format(DateTimeFormatter.BASIC_ISO_DATE)}" // 예: job_postings_geo_20240520 diff --git a/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/event/ChatRedisTemplate.kt b/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/event/ChatRedisTemplate.kt index 23e9463d..d1627402 100644 --- a/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/event/ChatRedisTemplate.kt +++ b/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/event/ChatRedisTemplate.kt @@ -15,37 +15,82 @@ class ChatRedisTemplate( private val objectMapper: ObjectMapper ) { + /** + * Removes a chat room from the set of unread chat rooms for a specific receiver in Redis. + * + * This indicates that the receiver has no unread messages in the specified chat room. + */ fun removeUnreadChatRoom(receiverId: String, chatRoomId: UUID) { val key = "unread_chatroom:${receiverId}" redisTemplate.opsForSet().remove(key, chatRoomId.toString()) } + /** + * Marks a chat room as having unread messages for a specific receiver by adding the chat room ID to the user's unread chat rooms set in Redis. + * + * @param receiverId The ID of the user who has unread messages. + * @param chatRoomId The ID of the chat room to add to the unread set. + */ fun addUnreadChatRoom(receiverId: String, chatRoomId: String) { val key = "unread_chatroom:${receiverId}" redisTemplate.opsForSet().add(key, chatRoomId) } + /** + * Updates the read message sequence number for a user in a specific chat room in Redis. + * + * Stores the provided message sequence as the latest read position for the user within the chat room. + */ fun updateReadSequence(chatRoomId: String, messageSequence: String, userId: UUID) { val key = "chatroom_read_sequence:$chatRoomId:$userId" redisTemplate.opsForValue().set(key, messageSequence.toLong()) } + /** + * Retrieves the read message sequence number for a user in a specific chat room from Redis. + * + * @return The last read message sequence number, or 0L if no value is found. + */ fun getReadSequence(userId: UUID, chatRoomId: UUID) : Long{ val key = "chatroom_read_sequence:$chatRoomId:$userId" return redisTemplate.opsForValue().get(key)?.toString()?.toLong() ?: 0L } + /** + * Increments and returns the current message sequence number for the specified chat room. + * + * If the sequence does not exist, it is initialized to 1. + * + * @param chatRoomId The identifier of the chat room. + * @return The incremented sequence number for the chat room. + */ fun getChatRoomSequence(chatRoomId: String): Long { val key = "chatroom_sequence:$chatRoomId" return redisTemplate.opsForValue().increment(key)?:1L } + /** + * Retrieves the set of chat room IDs with unread messages for the specified user. + * + * @param userId The unique identifier of the user. + * @return A set of chat room IDs where the user has unread messages, or an empty set if none exist. + */ fun getUnreadChatRooms(userId: UUID): Set { val key = "unread_chatroom:$userId" val members = redisTemplate.opsForSet().members(key) ?: return emptySet() return members.mapNotNull { it as? String }.toSet() } + /** + * Retrieves the read message sequence numbers for a user across multiple chat rooms. + * + * For each chat room ID, returns the last read message sequence number for the user. + * If no sequence is found for a chat room, the value defaults to 0L. + * + * @param userId The user's unique identifier. + * @param chatRoomIds The set of chat room IDs to query. + * @return A map of chat room ID to the user's read message sequence number. + */ fun getReadSequences(userId: UUID, chatRoomIds: Set): Map { if (chatRoomIds.isEmpty()) return emptyMap() @@ -58,6 +103,11 @@ class ChatRedisTemplate( } } + /** + * Checks whether the user has an active chat session by verifying the existence of a corresponding Redis key. + * + * @return `true` if the user has an active chat session; `false` otherwise. + */ fun isChatting(userId: UUID): Boolean { return redisTemplate.hasKey(userId.toString()) } diff --git a/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/repository/ChatMessageRepository.kt b/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/repository/ChatMessageRepository.kt index edd1cc91..bdca28ba 100644 --- a/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/repository/ChatMessageRepository.kt +++ b/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/repository/ChatMessageRepository.kt @@ -11,7 +11,14 @@ import java.util.* @Repository interface ChatMessageRepository : JpaRepository { - @Query(value = """ + /** + * Retrieves up to 50 most recent chat messages from a specific chat room with IDs less than the given message ID. + * + * @param chatroomId The unique identifier of the chat room. + * @param messageId The message ID threshold; only messages with IDs less than this value are returned. + * @return A list of up to 50 `ChatMessage` entities ordered by descending ID. + */ + @Query(value = """ SELECT * FROM chat_message WHERE chat_room_id = :chatroomId diff --git a/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/repository/ChatRoomRepository.kt b/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/repository/ChatRoomRepository.kt index 3bb19956..f90e3202 100644 --- a/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/repository/ChatRoomRepository.kt +++ b/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/repository/ChatRoomRepository.kt @@ -11,8 +11,21 @@ import java.util.* @Repository interface ChatRoomRepository : JpaRepository { - fun findByCarerIdAndCenterId(carerId: UUID, centerId: UUID): ChatRoom? + /** + * Retrieves a chat room matching the specified carer and center IDs. + * + * @param carerId The unique identifier of the carer. + * @param centerId The unique identifier of the center. + * @return The matching ChatRoom, or null if none exists. + */ +fun findByCarerIdAndCenterId(carerId: UUID, centerId: UUID): ChatRoom? + /** + * Retrieves summary information for the specified chat rooms, including details of the most recent message in each room. + * + * @param chatRoomIds Set of chat room UUIDs to query. + * @return A list of projections containing chat room details and the latest message for each specified chat room. + */ @Query(""" SELECT cr.id, diff --git a/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/vo/ChatRoomSummaryInfo.kt b/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/vo/ChatRoomSummaryInfo.kt index 7170e9ad..7e8ad68b 100644 --- a/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/vo/ChatRoomSummaryInfo.kt +++ b/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/vo/ChatRoomSummaryInfo.kt @@ -28,6 +28,12 @@ data class ChatRoomSummaryInfo( ) companion object { + /** + * Converts a 16-byte array into a UUID. + * + * @param array A byte array containing the most and least significant bits of a UUID. + * @return The UUID represented by the byte array. + */ fun fromByteArray(array: ByteArray): UUID { val buffer = ByteBuffer.wrap(array) val mostSigBits = buffer.long diff --git a/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/vo/ChatRoomSummaryInfoProjection.kt b/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/vo/ChatRoomSummaryInfoProjection.kt index 45e2895b..71250d78 100644 --- a/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/vo/ChatRoomSummaryInfoProjection.kt +++ b/idle-domain/src/main/kotlin/com/swm/idle/domain/chat/vo/ChatRoomSummaryInfoProjection.kt @@ -3,10 +3,38 @@ package com.swm.idle.domain.chat.vo import java.time.LocalDateTime interface ChatRoomSummaryInfoProjection { - fun getChatRoomId(): ByteArray - fun getCarerId(): ByteArray - fun getCenterId(): ByteArray - fun getLastMessage(): String - fun getLastMessageTime(): LocalDateTime - fun getLastSequence(): Long + /** + * Returns the unique identifier of the chat room as a byte array. + */ +fun getChatRoomId(): ByteArray + /** + * Returns the identifier of the carer associated with the chat room. + * + * @return A ByteArray representing the carer's unique identifier. + */ +fun getCarerId(): ByteArray + /** + * Returns the identifier of the center associated with the chat room. + * + * @return A ByteArray representing the center's unique identifier. + */ +fun getCenterId(): ByteArray + /** + * Returns the content of the last message sent in the chat room. + * + * @return The last message as a string. + */ +fun getLastMessage(): String + /** + * Returns the timestamp of the last message sent in the chat room. + * + * @return The date and time of the most recent message. + */ +fun getLastMessageTime(): LocalDateTime + /** + * Returns the sequence number of the last message in the chat room. + * + * @return The last message's sequence number. + */ +fun getLastSequence(): Long } \ No newline at end of file diff --git a/idle-domain/src/main/kotlin/com/swm/idle/domain/jobposting/repository/redis/RedisJobPostingRepository.kt b/idle-domain/src/main/kotlin/com/swm/idle/domain/jobposting/repository/redis/RedisJobPostingRepository.kt index 7eedd21d..7f451ffc 100644 --- a/idle-domain/src/main/kotlin/com/swm/idle/domain/jobposting/repository/redis/RedisJobPostingRepository.kt +++ b/idle-domain/src/main/kotlin/com/swm/idle/domain/jobposting/repository/redis/RedisJobPostingRepository.kt @@ -16,6 +16,17 @@ import java.util.* class RedisJobPostingRepository( private val redisTemplate: RedisTemplate, ) { + /** + * Retrieves a list of job posting UUIDs located within a specified distance from a given geographic point, with optional pagination. + * + * If `next` is null, returns up to `limit` UUIDs of job postings within the radius. If `next` is provided, returns the next page of UUIDs after the specified UUID, ordered by proximity. + * + * @param location The geographic point to search around. + * @param distance The search radius in kilometers. + * @param limit The maximum number of UUIDs to return. + * @param next The UUID to start pagination after, or null to start from the beginning. + * @return A list of job posting UUIDs matching the location and distance criteria, paginated if `next` is provided. + */ fun findByLocationAndDistance( location: Point, distance: Long, @@ -37,6 +48,11 @@ class RedisJobPostingRepository( return getPagedGeoUuids(keys, circle, next, limit) } + /** + * Generates Redis keys for job postings geospatial data for the current day and the previous 12 days. + * + * @return A list of Redis key strings in the format "job_postings_geo_YYYYMMDD" for the last 13 days. + */ private fun makeRedisKeys(): List { val today = LocalDate.now() val keys = (0..12).map { offset -> @@ -46,6 +62,16 @@ class RedisJobPostingRepository( return keys } + /** + * Retrieves all job posting UUIDs within the specified geospatial circle across multiple Redis keys. + * + * Iterates through the provided Redis keys, performing a geospatial radius query for each, + * and collects UUIDs of job postings located within the given circle. + * + * @param keys List of Redis keys to search for job postings. + * @param circle Geospatial area to search within. + * @return Mutable list of UUIDs for job postings found within the specified area. + */ private fun findPostingIds( keys: List, circle: Circle @@ -63,6 +89,17 @@ class RedisJobPostingRepository( return ids } + /** + * Retrieves a paginated list of job posting UUIDs within a specified geospatial area, starting after a given UUID. + * + * Collects UUIDs from the provided Redis keys that fall within the given circle, sorts them by ascending distance from the center, and returns up to the specified limit of UUIDs following the `next` UUID. + * + * @param keys List of Redis keys to search for geospatial job postings. + * @param circle The geospatial area to search within. + * @param next The UUID after which to start pagination. + * @param limit The maximum number of UUIDs to return. + * @return A list of UUIDs representing job postings after the `next` UUID, ordered by proximity. + */ fun getPagedGeoUuids( keys: List, circle: Circle, diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/batch/api/BatchApi.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/batch/api/BatchApi.kt index 9e6e87f4..4d03cca5 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/batch/api/BatchApi.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/batch/api/BatchApi.kt @@ -12,6 +12,11 @@ import org.springframework.web.bind.annotation.ResponseStatus @RequestMapping("/api/v2/batch", produces = ["application/json;charset=utf-8"]) interface BatchApi { + /** + * Triggers the execution of the batch endpoint. + * + * @param day Optional day value to specify the batch execution context. Defaults to 0 if not provided. + */ @Hidden @Operation(summary = "배치 엔트포인트 실행 API") @GetMapping diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/batch/controller/BatchController.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/batch/controller/BatchController.kt index 70b7d11d..07e77e72 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/batch/controller/BatchController.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/batch/controller/BatchController.kt @@ -8,5 +8,10 @@ import org.springframework.web.bind.annotation.RestController class BatchController( private val jobLauncher: CrawlingJobLauncher, ) : BatchApi { - override fun launchBatch(day: Long) = jobLauncher.jobStart(day) + /** + * Initiates a batch crawling job for the specified day. + * + * @param day The day for which the batch job should be started, represented as a long value. + */ +override fun launchBatch(day: Long) = jobLauncher.jobStart(day) } \ No newline at end of file diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/api/ChatCarerApi.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/api/ChatCarerApi.kt index 4734e1c8..425fe67b 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/api/ChatCarerApi.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/api/ChatCarerApi.kt @@ -21,7 +21,14 @@ interface ChatCarerApi { @ResponseStatus(HttpStatus.OK) fun createChatroom(request: CreateChatRoomRequest): CreateChatRoomResponse - @Secured + /** + * Retrieves recent chat messages for the specified chat room, optionally starting from a given message ID. + * + * @param chatroomId The unique identifier of the chat room. + * @param messageId An optional message ID to fetch messages after this point. + * @return A response containing recent chat messages for the chat room. + */ + @Secured @Operation(summary = "보호사의 최근 채팅 메시지 조회 API") @GetMapping("/chatrooms/{chatroom-id}/messages") @ResponseStatus(HttpStatus.OK) diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/api/ChatCenterApi.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/api/ChatCenterApi.kt index 0dee53bc..b6aad5cb 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/api/ChatCenterApi.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/api/ChatCenterApi.kt @@ -21,7 +21,14 @@ interface ChatCenterApi { @ResponseStatus(HttpStatus.OK) fun createChatroom(request: CreateChatRoomRequest): CreateChatRoomResponse - @Secured + /** + * Retrieves the most recent chat message for the specified chat room, optionally starting from a given message ID. + * + * @param chatroomId The unique identifier of the chat room. + * @param messageId An optional message ID to specify the starting point for retrieval. + * @return The most recent chat message in the chat room. + */ + @Secured @Operation(summary = "센터장의 최근 채팅 메시지 조회 API") @GetMapping("/chatrooms/{chatroom-id}/messages") @ResponseStatus(HttpStatus.OK) diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatCarerController.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatCarerController.kt index dec5aa36..4e3eb491 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatCarerController.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatCarerController.kt @@ -18,10 +18,22 @@ class ChatCarerController( return chatMessageService.createChatroom(request,true) } + /** + * Retrieves a list of chat room summaries for the carer. + * + * @return A list of chat room summary information relevant to the carer. + */ override fun carerChatroomSummary(): List { return chatMessageService.getChatroomSummary(true) } + /** + * Retrieves recent chat messages for a specific chatroom, optionally starting from a given message ID. + * + * @param chatroomId The unique identifier of the chatroom. + * @param messageId The ID of the message to start retrieving from, or null to retrieve the most recent messages. + * @return A response containing recent chat messages for the specified chatroom. + */ override fun recentMessages(chatroomId: UUID, messageId: UUID?): ChatMessageResponse { return chatMessageService.getRecentMessages(chatroomId, messageId, true) } diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatCenterController.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatCenterController.kt index 6b3b1173..33779e84 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatCenterController.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatCenterController.kt @@ -18,10 +18,22 @@ class ChatCenterController( return chatMessageService.createChatroom(request,false) } + /** + * Retrieves summary information for all chat rooms in the center. + * + * @return A list of chat room summary information. + */ override fun centerChatroomSummary(): List { return chatMessageService.getChatroomSummary(false) } + /** + * Retrieves recent chat messages for a specified chat room. + * + * @param chatroomId The unique identifier of the chat room. + * @param messageId An optional message ID to fetch messages after this point. + * @return A response containing recent chat messages for the given chat room. + */ override fun recentMessages(chatroomId: UUID, messageId: UUID?): ChatMessageResponse { return chatMessageService.getRecentMessages(chatroomId, messageId, false) } diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatSocketController.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatSocketController.kt index 3566e265..b383b713 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatSocketController.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/controller/ChatSocketController.kt @@ -14,6 +14,11 @@ class ChatSocketController ( private val chatMessageService: ChatFacadeService, ) { + /** + * Handles chat message sending requests from carers via the "/send/carer" WebSocket endpoint. + * + * Extracts the carer's user ID from the WebSocket session and delegates message sending to the chat service. + */ @MessageMapping("/send/carer") fun carerSendMessage(@Payload request: SendChatMessageRequest, headerAccessor: SimpMessageHeaderAccessor) { @@ -21,6 +26,11 @@ class ChatSocketController ( chatMessageService.send(request, userId, true) } + /** + * Marks chat messages as read for a carer user. + * + * Processes a read receipt request sent to the "/read/carer" WebSocket endpoint, identifying the user from the session and updating message read status as a carer. + */ @MessageMapping("/read/carer") fun carerRead(@Payload request: ReadChatMessagesReqeust, headerAccessor: SimpMessageHeaderAccessor) { @@ -28,6 +38,11 @@ class ChatSocketController ( chatMessageService.read(request, userId, true) } + /** + * Handles chat message sending requests from center users via the "/send/center" WebSocket endpoint. + * + * Extracts the center user's ID from the WebSocket session and delegates the message sending to the chat message service. + */ @MessageMapping("/send/center") fun centerSendMessage(@Payload request: SendChatMessageRequest, headerAccessor: SimpMessageHeaderAccessor) { @@ -35,6 +50,11 @@ class ChatSocketController ( chatMessageService.send(request, userId, false) } + /** + * Marks chat messages as read for a center user based on the provided request. + * + * @param request The request containing information about which messages to mark as read. + */ @MessageMapping("/read/center") fun centerRead(@Payload request: ReadChatMessagesReqeust, headerAccessor: SimpMessageHeaderAccessor) { diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/handler/ChatHandler.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/handler/ChatHandler.kt index 5c918f01..915063b6 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/handler/ChatHandler.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/chat/handler/ChatHandler.kt @@ -12,12 +12,22 @@ import org.springframework.stereotype.Controller class ChatHandler( private val messageTemplate: SimpMessageSendingOperations, ) { + /** + * Handles chat message events by sending the message to both the receiver and sender via WebSocket. + * + * Sends a `ChatMessageSocketResponse` containing the chat message to the appropriate subscription endpoints for real-time delivery. + */ @EventListener fun handleSendMessage(sendMessage: ChatMessage) { messageTemplate.convertAndSend("/sub/${sendMessage.receiverId}", ChatMessageSocketResponse(sendMessage)) messageTemplate.convertAndSend("/sub/${sendMessage.senderId}", ChatMessageSocketResponse(sendMessage)) } + /** + * Sends a read notification to both the receiver and the user who read the message via WebSocket. + * + * Listens for `ReadMessage` events and dispatches a `ReadNoti` to the appropriate WebSocket destinations for real-time notification. + */ @EventListener fun handleReadMessage(readMessage: ReadMessage) { messageTemplate.convertAndSend("/sub/${readMessage.receiverId}", ReadNoti(readMessage)) diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/api/CrawlingJobPostingApi.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/api/CrawlingJobPostingApi.kt index 47a85dcf..1e91996a 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/api/CrawlingJobPostingApi.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/api/CrawlingJobPostingApi.kt @@ -24,6 +24,12 @@ interface CrawlingJobPostingApi { @ResponseStatus(HttpStatus.OK) fun getCrawlingJobPostingDetail(@PathVariable(value = "crawling-job-posting-id") crawlingJobPostingId: UUID): CrawlingJobPostingResponse + /** + * Retrieves a paginated list of crawling job postings based on the provided cursor scroll request. + * + * @param request The cursor scroll request containing pagination and filtering criteria. + * @return A response containing the list of crawling job postings and pagination information. + */ @Secured @Operation(summary = "크롤링 공고 전체 조회 API") @GetMapping diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CarerJobPostingController.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CarerJobPostingController.kt index d7421b3c..1f8f6995 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CarerJobPostingController.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CarerJobPostingController.kt @@ -19,20 +19,46 @@ class CarerJobPostingController( private val jobPostingFavoriteFacadeService: JobPostingFavoriteFacadeService, ) : CarerJobPostingApi { + /** + * Retrieves detailed information about a specific job posting. + * + * @param jobPostingId The unique identifier of the job posting. + * @return The detailed response for the specified job posting. + */ override fun getJobPosting(jobPostingId: UUID): CarerJobPostingResponse { return carerJobPostingFacadeService.getJobPostingDetail(jobPostingId) } + /** + * Retrieves a paginated list of carer job postings based on the provided cursor scroll request. + * + * @param request Cursor-based pagination parameters for fetching job postings. + * @return A scrollable response containing carer job postings within the specified range. + */ override fun getJobPostings(request: CursorScrollRequest): CarerJobPostingScrollResponse { return carerJobPostingFacadeService.getJobPostingsInRange(request) } + /** + * Retrieves a paginated list of job postings to which the current user has applied. + * + * @param request Cursor-based pagination parameters for scrolling through applied job postings. + * @return A scrollable response containing the user's applied job postings. + */ override fun getAppliedJobPostings( request: CursorScrollRequest, ): CarerAppliedJobPostingScrollResponse { return carerJobPostingFacadeService.getAppliedJobPostings(request) } + /** + * Marks a job posting as a favorite for the currently authenticated carer. + * + * Associates the specified job posting with the current user's favorites using the provided job posting type. + * + * @param jobPostingId The unique identifier of the job posting to be favorited. + * @param request Contains the job posting type for the favorite action. + */ override fun createJobPostingFavorite( jobPostingId: UUID, request: CreateJobPostingFavoriteRequest, @@ -44,6 +70,11 @@ class CarerJobPostingController( ) } + /** + * Removes the specified job posting from the current user's list of favorites. + * + * @param jobPostingId The unique identifier of the job posting to unfavorite. + */ override fun deleteJobPostingFavorite(jobPostingId: UUID) { jobPostingFavoriteFacadeService.deleteJobPostingFavorite( carerId = getUserAuthentication().userId, @@ -51,6 +82,11 @@ class CarerJobPostingController( ) } + /** + * Retrieves the list of job postings favorited by the current user. + * + * @return The user's favorite job postings. + */ override fun getMyFavoriteJobPostings(): JobPostingFavoriteResponse { return carerJobPostingFacadeService.getMyFavoriteJobPostings() } diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CenterJobPostingController.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CenterJobPostingController.kt index a0fc0f1d..94651f89 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CenterJobPostingController.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CenterJobPostingController.kt @@ -16,14 +16,30 @@ class CenterJobPostingController( private val centerJobPostingFacadeService: CenterPostingFacadeService, ) : CenterJobPostingApi { + /** + * Creates a new job posting with the provided request data. + * + * Initiates the creation process asynchronously by delegating to the facade service. + */ override suspend fun createJobPosting(request: CreateJobPostingRequest) { centerJobPostingFacadeService.create(request = request) } + /** + * Updates an existing job posting with new information. + * + * @param jobPostingId The unique identifier of the job posting to update. + * @param request The update request containing new job posting details. + */ override fun updateJobPosting(jobPostingId: UUID, request: UpdateJobPostingRequest) { centerJobPostingFacadeService.update(jobPostingId, request) } + /** + * Deletes the job posting identified by the given UUID. + * + * @param jobPostingId The unique identifier of the job posting to delete. + */ override fun deleteJobPosting(jobPostingId: UUID) { centerJobPostingFacadeService.delete(jobPostingId) } diff --git a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CrawlingJobPostingController.kt b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CrawlingJobPostingController.kt index 17efcf62..d5ce8c7e 100644 --- a/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CrawlingJobPostingController.kt +++ b/idle-presentation/src/main/kotlin/com/swm/idle/presentation/jobposting/controller/CrawlingJobPostingController.kt @@ -14,14 +14,31 @@ class CrawlingJobPostingController( private val crawlingJobPostingFacadeService: CrawlingPostingFacadeService, ) : CrawlingJobPostingApi { + /** + * Retrieves detailed information for a specific crawling job posting. + * + * @param crawlingJobPostingId The unique identifier of the crawling job posting. + * @return The detailed response for the specified crawling job posting. + */ override fun getCrawlingJobPostingDetail(crawlingJobPostingId: UUID): CrawlingJobPostingResponse { return crawlingJobPostingFacadeService.getCrawlingJobPosting(crawlingJobPostingId) } + /** + * Retrieves a paginated list of crawling job postings based on the provided cursor scroll request. + * + * @param request The cursor-based scroll request specifying pagination and filtering criteria. + * @return A scroll response containing the list of crawling job postings and pagination information. + */ override fun getCrawlingJobPostings(request: CrawlingCursorScrollRequest): CrawlingJobPostingScrollResponse { return crawlingJobPostingFacadeService.getCrawlingPostingsInRange(request) } + /** + * Retrieves the list of favorite crawling job postings. + * + * @return A response containing favorite crawling job postings. + */ override fun getFavoriteCrawlingJobPostings(): CrawlingJobPostingFavoriteResponse { return crawlingJobPostingFacadeService.getFavoriteCrawlingJobPostings() } diff --git a/idle-support/transfer/src/main/kotlin/com/swm/idle/support/transfer/jobposting/carer/CrawlingJobPostingScrollResponse.kt b/idle-support/transfer/src/main/kotlin/com/swm/idle/support/transfer/jobposting/carer/CrawlingJobPostingScrollResponse.kt index b7a7b2ab..54296298 100644 --- a/idle-support/transfer/src/main/kotlin/com/swm/idle/support/transfer/jobposting/carer/CrawlingJobPostingScrollResponse.kt +++ b/idle-support/transfer/src/main/kotlin/com/swm/idle/support/transfer/jobposting/carer/CrawlingJobPostingScrollResponse.kt @@ -76,6 +76,15 @@ data class CrawlingJobPostingScrollResponse( companion object { + /** + * Creates a `CrawlingJobPostingScrollResponse` from a list of job posting previews and a distance value. + * + * Converts each preview DTO to a `CrawlingJobPostingDto`, sets the `next` value to the last item's ID (or null if the list is empty), assigns the total number of items, and sets the next distance in kilometers. + * + * @param items List of job posting preview DTOs to include in the response. + * @param distance Distance in kilometers to the next item for pagination. + * @return A populated `CrawlingJobPostingScrollResponse` representing the paginated job postings. + */ fun from( items: List, distance: Long diff --git a/idle-support/transfer/src/main/kotlin/com/swm/idle/support/transfer/jobposting/common/CrawlingJobPostingResponse.kt b/idle-support/transfer/src/main/kotlin/com/swm/idle/support/transfer/jobposting/common/CrawlingJobPostingResponse.kt index 21f18c25..a8a9d060 100644 --- a/idle-support/transfer/src/main/kotlin/com/swm/idle/support/transfer/jobposting/common/CrawlingJobPostingResponse.kt +++ b/idle-support/transfer/src/main/kotlin/com/swm/idle/support/transfer/jobposting/common/CrawlingJobPostingResponse.kt @@ -75,6 +75,15 @@ data class CrawlingJobPostingResponse( ) { companion object { + /** + * Creates a [CrawlingJobPostingResponse] from a [CrawledJobPosting] entity. + * + * Maps the fields from the given job posting entity to a response object, including favorite status and distance. + * + * @param isFavorite Indicates whether the job posting is marked as favorite. + * @param distance Distance to the job posting location in meters. + * @return A [CrawlingJobPostingResponse] representing the detailed job posting information. + */ fun from( posting: CrawledJobPosting, isFavorite: Boolean,