@@ -4,12 +4,14 @@ protocol DiaryServicing {
44 func fetchMyDiaries( page: Int , size: Int ) async throws -> MyDiaryFeedsResponse
55 func fetchMyFeeds( page: Int , size: Int ) async throws -> MyDiaryFeedsResponse
66 func fetchUserFeeds( userId: Int , page: Int , size: Int ) async throws -> UserDiaryFeedsResponse
7+ func fetchDiaryLikeUsers( diaryId: Int , searchCond: String ? , page: Int , size: Int ) async throws -> UserSearchResponse
78 func createDiary( request: DiaryCreateRequest ) async throws -> DiaryCreateResult
89 func updateDiary( diaryId: Int , request: DiaryUpdateRequest ) async throws
910 func deleteDiary( diaryId: Int ) async throws
1011 func updateMyDiaryOrder( request: DiaryOrderUpdateRequest ) async throws
1112 func toggleDiaryLike( diaryId: Int ) async throws -> DiaryLikeToggleResponse
1213 func toggleDiaryStore( diaryId: Int ) async throws -> DiaryStoreToggleResponse
14+ func reportDiary( diaryId: Int , content: String ) async throws
1315}
1416
1517enum DiaryServiceError : LocalizedError {
@@ -124,6 +126,40 @@ struct DiaryService: DiaryServicing {
124126 }
125127 }
126128
129+ func fetchDiaryLikeUsers(
130+ diaryId: Int ,
131+ searchCond: String ? = nil ,
132+ page: Int = Self . defaultPage,
133+ size: Int = Self . defaultSize
134+ ) async throws -> UserSearchResponse {
135+ let resolvedPage = max ( page, Self . defaultPage)
136+ let resolvedSize = size > 0 ? size : Self . defaultSize
137+ let trimmedSearchCond = searchCond? . trimmingCharacters ( in: . whitespacesAndNewlines) ?? " "
138+
139+ var queryItems = [
140+ URLQueryItem ( name: " page " , value: String ( resolvedPage) ) ,
141+ URLQueryItem ( name: " size " , value: String ( resolvedSize) )
142+ ]
143+
144+ if !trimmedSearchCond. isEmpty {
145+ queryItems. insert ( URLQueryItem ( name: " searchCond " , value: trimmedSearchCond) , at: 0 )
146+ }
147+
148+ do {
149+ let request = APIRequest (
150+ path: " /diaries/ \( diaryId) /like " ,
151+ method: . get,
152+ queryItems: queryItems,
153+ requiresAuthorization: true
154+ )
155+ let response = try await apiClient. request ( request, responseType: UserSearchResponseDTO . self)
156+ return response. toModel ( )
157+ } catch {
158+ if isRequestCancelled ( error) { throw error }
159+ throw mapError ( error)
160+ }
161+ }
162+
127163 func createDiary( request: DiaryCreateRequest ) async throws -> DiaryCreateResult {
128164 let requestBody : Data
129165 do {
@@ -244,6 +280,32 @@ struct DiaryService: DiaryServicing {
244280 }
245281 }
246282
283+ func reportDiary( diaryId: Int , content: String ) async throws {
284+ let requestBody : Data
285+ do {
286+ requestBody = try JSONEncoder ( ) . encode (
287+ DiaryReportRequest ( content: content. trimmingCharacters ( in: . whitespacesAndNewlines) )
288+ )
289+ } catch {
290+ throw DiaryServiceError . requestEncodingFailed
291+ }
292+
293+ do {
294+ var request = APIRequest (
295+ path: " /diaries/ \( diaryId) /reports " ,
296+ method: . post,
297+ requiresAuthorization: true ,
298+ body: requestBody
299+ )
300+ request. headers [ " Accept " ] = " application/json "
301+ request. headers [ " Content-Type " ] = " application/json "
302+ try await apiClient. request ( request)
303+ } catch {
304+ if isRequestCancelled ( error) { throw error }
305+ throw mapError ( error)
306+ }
307+ }
308+
247309 private func mapError( _ error: Error ) -> DiaryServiceError {
248310 if let diaryServiceError = error as? DiaryServiceError {
249311 return diaryServiceError
@@ -256,7 +318,10 @@ struct DiaryService: DiaryServicing {
256318 case . missingAccessToken, . missingRefreshToken, . unauthorized:
257319 return . sessionExpired
258320 case . serverError( let statusCode, let message) :
259- return . serverError( statusCode: statusCode, message: message)
321+ return . serverError(
322+ statusCode: statusCode,
323+ message: normalizeServerErrorMessage ( message)
324+ )
260325 case . decodingFailed:
261326 return . decodingFailed
262327 }
@@ -265,6 +330,41 @@ struct DiaryService: DiaryServicing {
265330 return . networkFailure( message: " 네트워크 요청 중 오류가 발생했어요. " )
266331 }
267332
333+ private func normalizeServerErrorMessage( _ rawMessage: String ? ) -> String ? {
334+ guard
335+ let rawMessage = rawMessage? . trimmingCharacters ( in: . whitespacesAndNewlines) ,
336+ !rawMessage. isEmpty
337+ else {
338+ return nil
339+ }
340+
341+ guard
342+ rawMessage. first == " { " ,
343+ let data = rawMessage. data ( using: . utf8) ,
344+ let parsed = try ? JSONDecoder ( ) . decode ( DiaryServiceErrorResponse . self, from: data)
345+ else {
346+ return rawMessage
347+ }
348+
349+ if let message = parsed. message? . trimmingCharacters ( in: . whitespacesAndNewlines) ,
350+ !message. isEmpty {
351+ return message
352+ }
353+
354+ let fieldMessages = ( parsed. fieldErrors ?? [ ] )
355+ . flatMap ( \. values)
356+ . map { $0. trimmingCharacters ( in: . whitespacesAndNewlines) }
357+ . filter { !$0. isEmpty }
358+
359+ let globalMessages = ( parsed. globalErrors ?? [ ] )
360+ . map { $0. trimmingCharacters ( in: . whitespacesAndNewlines) }
361+ . filter { !$0. isEmpty }
362+
363+ let merged = fieldMessages + globalMessages
364+ guard !merged. isEmpty else { return rawMessage }
365+ return merged. joined ( separator: " \n " )
366+ }
367+
268368 private func extractDiaryID( from location: String ? ) -> Int ? {
269369 guard let location else { return nil }
270370 let trimmed = location. trimmingCharacters ( in: . whitespacesAndNewlines)
@@ -284,3 +384,9 @@ struct DiaryService: DiaryServicing {
284384 return nsError. domain == NSURLErrorDomain && nsError. code == NSURLErrorCancelled
285385 }
286386}
387+
388+ private struct DiaryServiceErrorResponse : Decodable {
389+ let message : String ?
390+ let fieldErrors : [ [ String : String ] ] ?
391+ let globalErrors : [ String ] ?
392+ }
0 commit comments