tenantId(); $userId = $this->apiUserId(); $query = TodayIssue::query() ->where('tenant_id', $tenantId) ->forUser($userId) // 본인 대상 또는 전체 브로드캐스트 ->active() // 만료되지 않은 이슈만 ->today() // 오늘 날짜 이슈만 ->orderByDesc('created_at'); // 뱃지 필터 if ($badge !== null && $badge !== 'all') { $query->byBadge($badge); } // 전체 개수 (필터 적용 전, 오늘 날짜만) $totalQuery = TodayIssue::query() ->where('tenant_id', $tenantId) ->forUser($userId) ->active() ->today(); $totalCount = $totalQuery->count(); // 결과 조회 $issues = $query->limit($limit)->get(); $items = $issues->map(function (TodayIssue $issue) { // source_type 기반으로 badge 매핑 (DB 값보다 우선) $badge = TodayIssue::SOURCE_TO_BADGE[$issue->source_type] ?? $issue->badge ?? '기타'; return [ 'id' => $issue->source_type.'_'.$issue->source_id, 'badge' => $badge, 'content' => $issue->content, 'time' => $this->formatRelativeTime($issue->created_at), 'date' => $issue->created_at?->toDateString(), 'needsApproval' => $issue->needs_approval, 'path' => $issue->path, ]; })->toArray(); return [ 'items' => $items, 'total_count' => $totalCount, ]; } /** * 읽지 않은 이슈 목록 조회 (헤더 알림용) * * @param int $limit 조회할 최대 항목 수 (기본 10) */ public function getUnreadList(int $limit = 10): array { $tenantId = $this->tenantId(); $userId = $this->apiUserId(); $issues = TodayIssue::query() ->where('tenant_id', $tenantId) ->forUser($userId) // 본인 대상 또는 전체 브로드캐스트 ->unread() ->active() ->orderByDesc('created_at') ->limit($limit) ->get(); $totalCount = TodayIssue::query() ->where('tenant_id', $tenantId) ->forUser($userId) ->unread() ->active() ->count(); $items = $issues->map(function (TodayIssue $issue) { // source_type 기반으로 badge 매핑 (DB 값보다 우선) $badge = TodayIssue::SOURCE_TO_BADGE[$issue->source_type] ?? $issue->badge ?? '기타'; return [ 'id' => $issue->id, 'badge' => $badge, 'notification_type' => $issue->notification_type, 'content' => $issue->content, 'path' => $issue->path, 'needs_approval' => $issue->needs_approval, 'time' => $this->formatRelativeTime($issue->created_at), 'created_at' => $issue->created_at?->toIso8601String(), ]; })->toArray(); return [ 'items' => $items, 'total' => $totalCount, ]; } /** * 읽지 않은 이슈 개수 조회 (헤더 알림 뱃지용) */ public function getUnreadCount(): int { $tenantId = $this->tenantId(); $userId = $this->apiUserId(); return TodayIssue::query() ->where('tenant_id', $tenantId) ->forUser($userId) // 본인 대상 또는 전체 브로드캐스트 ->unread() ->active() ->count(); } /** * 이슈 확인 처리 */ public function markAsRead(int $issueId): bool { $tenantId = $this->tenantId(); $userId = $this->apiUserId(); $issue = TodayIssue::where('tenant_id', $tenantId) ->forUser($userId) // 본인 대상 또는 전체 브로드캐스트 ->where('id', $issueId) ->first(); if (! $issue) { return false; } return $issue->markAsRead($userId); } /** * 모든 이슈 읽음 처리 */ public function markAllAsRead(): int { $tenantId = $this->tenantId(); $userId = $this->apiUserId(); return TodayIssue::query() ->where('tenant_id', $tenantId) ->forUser($userId) // 본인 대상 또는 전체 브로드캐스트 ->unread() ->active() ->update([ 'is_read' => true, 'read_by' => $userId, 'read_at' => now(), ]); } /** * 이슈 삭제 (확인 완료 처리) */ public function dismiss(string $sourceType, int $sourceId): bool { $tenantId = $this->tenantId(); return TodayIssue::removeBySource($tenantId, $sourceType, $sourceId); } /** * 뱃지별 개수 조회 */ public function countByBadge(): array { $tenantId = $this->tenantId(); $userId = $this->apiUserId(); $counts = TodayIssue::query() ->where('tenant_id', $tenantId) ->forUser($userId) // 본인 대상 또는 전체 브로드캐스트 ->active() ->selectRaw('badge, COUNT(*) as count') ->groupBy('badge') ->pluck('count', 'badge') ->toArray(); // 전체 개수 추가 $counts['all'] = array_sum($counts); return $counts; } /** * 만료된 이슈 정리 (스케줄러에서 호출) */ public function cleanupExpiredIssues(): int { return TodayIssue::where('expires_at', '<', now())->delete(); } /** * 등록 시간 포맷팅 * - 오늘: "14:30" * - 어제: "어제 14:30" * - 그 외: "1/22 14:30" */ private function formatRelativeTime(?Carbon $datetime): string { if (! $datetime) { return ''; } $now = Carbon::now(); $time = $datetime->format('H:i'); // 오늘 if ($datetime->isToday()) { return $time; } // 어제 if ($datetime->isYesterday()) { return __('message.today_issue.time_yesterday').' '.$time; } // 그 외 return $datetime->format('n/j').' '.$time; } }