@@ -439,5 +439,172 @@ export const CommissionService = {
439439 } ) ;
440440 }
441441 }
442+ } ,
443+
444+ /**
445+ * 커미션 게시글 작가 정보 조회
446+ */
447+ async getCommissionArtistInfo ( userId , dto ) {
448+ const { commissionId, page, limit } = dto ;
449+
450+ // 1. 커미션 존재 여부 확인 및 작가 정보 조회
451+ const commission = await CommissionRepository . findArtistInfoByCommissionId ( commissionId , userId ) ;
452+ if ( ! commission ) {
453+ throw new CommissionNotFoundError ( { commissionId } ) ;
454+ }
455+
456+ const artistId = commission . artist . id ;
457+
458+ // 2. 작가 통계 정보 조회 (병렬 처리)
459+ const [ followerCount , completedWorksCount , reviewStats , reviews , totalReviews ] = await Promise . all ( [
460+ CommissionRepository . countFollowersByArtistId ( artistId ) ,
461+ CommissionRepository . countCompletedWorksByArtistId ( artistId ) ,
462+ CommissionRepository . getReviewStatsByArtistId ( artistId ) ,
463+ CommissionRepository . findReviewsByArtistId ( artistId , page , limit ) ,
464+ CommissionRepository . countReviewsByArtistId ( artistId )
465+ ] ) ;
466+
467+ // 3. 리뷰 통계 계산
468+ const { averageRate, recommendationRate } = this . calculateReviewStatistics ( reviewStats ) ;
469+
470+ // 4. 리뷰 데이터 처리 (이미지 포함)
471+ const processedReviews = await this . processReviews ( reviews ) ;
472+
473+ // 5. 페이지네이션 정보 계산
474+ const totalPages = Math . ceil ( totalReviews / limit ) ;
475+
476+ return {
477+ artist : {
478+ artistId : commission . artist . id ,
479+ nickname : commission . artist . nickname ,
480+ profileImageUrl : commission . artist . profileImage ,
481+ follower : followerCount ,
482+ completedworks : completedWorksCount
483+ } ,
484+ isFollowing : commission . artist . follows ?. length > 0 || false ,
485+ reviewStatistics : {
486+ averageRate : averageRate ,
487+ totalReviews : totalReviews ,
488+ recommendationRate : recommendationRate
489+ } ,
490+ recentReviews : processedReviews ,
491+ pagination : {
492+ page : page ,
493+ limit : limit ,
494+ total : totalReviews ,
495+ totalPages : totalPages
496+ }
497+ } ;
498+ } ,
499+
500+ /**
501+ * 리뷰 통계 계산
502+ */
503+ calculateReviewStatistics ( reviewStats ) {
504+ if ( reviewStats . length === 0 ) {
505+ return {
506+ averageRate : 0.0 ,
507+ recommendationRate : 0
508+ } ;
509+ }
510+
511+ // 평균 별점 계산 (소수점 1자리)
512+ const totalRate = reviewStats . reduce ( ( sum , review ) => sum + review . rate , 0 ) ;
513+ const averageRate = Math . round ( ( totalRate / reviewStats . length ) * 10 ) / 10 ;
514+
515+ // 추천율 계산 (평균별점 * 20)
516+ const recommendationRate = Math . round ( averageRate * 20 ) ;
517+
518+ return {
519+ averageRate,
520+ recommendationRate
521+ } ;
522+ } ,
523+
524+ /**
525+ * 리뷰 데이터 처리 (이미지, 작업기간, 상대시간 포함)
526+ */
527+ async processReviews ( reviews ) {
528+ const processedReviews = [ ] ;
529+
530+ for ( const review of reviews ) {
531+ // 리뷰 이미지 조회
532+ const images = await CommissionRepository . findImagesByReviewId ( review . id ) ;
533+
534+ // 작업 기간 계산
535+ const workperiod = this . calculateWorkPeriod (
536+ review . request . inProgressAt ,
537+ review . request . completedAt
538+ ) ;
539+
540+ // 상대 시간 계산
541+ const timeAgo = this . calculateTimeAgo ( review . createdAt ) ;
542+
543+ processedReviews . push ( {
544+ id : review . id ,
545+ rate : review . rate ,
546+ content : review . content ,
547+ userNickname : review . user . nickname ,
548+ commissionTitle : review . request . commission . title ,
549+ workperiod : workperiod ,
550+ createdAt : review . createdAt . toISOString ( ) ,
551+ timeAgo : timeAgo ,
552+ images : images . map ( img => ( {
553+ id : img . id ,
554+ imageUrl : img . imageUrl ,
555+ orderIndex : img . orderIndex
556+ } ) )
557+ } ) ;
558+ }
559+
560+ return processedReviews ;
561+ } ,
562+
563+ /**
564+ * 작업 기간 계산
565+ */
566+ calculateWorkPeriod ( inProgressAt , completedAt ) {
567+ if ( ! inProgressAt || ! completedAt ) {
568+ return null ;
569+ }
570+
571+ const diffMs = new Date ( completedAt ) - new Date ( inProgressAt ) ;
572+ const diffDays = Math . floor ( diffMs / ( 1000 * 60 * 60 * 24 ) ) ;
573+ const diffHours = Math . floor ( diffMs / ( 1000 * 60 * 60 ) ) ;
574+ const diffMinutes = Math . floor ( diffMs / ( 1000 * 60 ) ) ;
575+
576+ if ( diffDays >= 30 ) {
577+ const months = Math . floor ( diffDays / 30 ) ;
578+ return `${ months } 달` ;
579+ } else if ( diffDays >= 1 ) {
580+ return `${ diffDays } 일` ;
581+ } else if ( diffHours >= 1 ) {
582+ return `${ diffHours } 시간` ;
583+ } else {
584+ return `${ diffMinutes } 분` ;
585+ }
586+ } ,
587+
588+ /**
589+ * 상대 시간 계산 (n일 전, n달 전)
590+ */
591+ calculateTimeAgo ( createdAt ) {
592+ const now = new Date ( ) ;
593+ const created = new Date ( createdAt ) ;
594+ const diffMs = now - created ;
595+ const diffDays = Math . floor ( diffMs / ( 1000 * 60 * 60 * 24 ) ) ;
596+ const diffHours = Math . floor ( diffMs / ( 1000 * 60 * 60 ) ) ;
597+ const diffMinutes = Math . floor ( diffMs / ( 1000 * 60 ) ) ;
598+
599+ if ( diffDays >= 30 ) {
600+ const months = Math . floor ( diffDays / 30 ) ;
601+ return `${ months } 달 전` ;
602+ } else if ( diffDays >= 1 ) {
603+ return `${ diffDays } 일 전` ;
604+ } else if ( diffHours >= 1 ) {
605+ return `${ diffHours } 시간 전` ;
606+ } else {
607+ return `${ diffMinutes } 분 전` ;
608+ }
442609 }
443610} ;
0 commit comments