feat(m5): notifications & activity log foundation
- Expand NotificationRepository (unread list paginated, count, markRead ownership-scoped); new ActivityLogRepository (create, findByProject paginated) with deterministic created_at+id ordering - NotificationService (notifyOnEvent, resolveTargets for 8 event types, injectable SseBroadcaster for M8) + ActivityLogService (logActivity, listByProject) - NotificationEvent/SseEvent/SseBroadcaster types in lib/types - APIs: GET /api/notifications, POST /api/notifications/:id/read, GET /api/projects/:projectId/activity - Screens: notifications list + MarkReadButton, NotificationBadge in Header, project activity page + ProjectNav link - Unit tests for both repositories and services (28 new)
This commit is contained in:
@ -2,8 +2,11 @@ import { getDb } from '@/lib/db/sqlite';
|
||||
import { ProjectRepository } from '@/repositories/ProjectRepository';
|
||||
import { ProjectMemberRepository } from '@/repositories/ProjectMemberRepository';
|
||||
import { NotificationRepository } from '@/repositories/NotificationRepository';
|
||||
import { ActivityLogRepository } from '@/repositories/ActivityLogRepository';
|
||||
import { UserRepository } from '@/repositories/UserRepository';
|
||||
import { ProjectService } from '@/services/ProjectService';
|
||||
import { NotificationService } from '@/services/NotificationService';
|
||||
import { ActivityLogService } from '@/services/ActivityLogService';
|
||||
|
||||
/**
|
||||
* Route Handler用に各Repository/Serviceを構築するファクトリ。
|
||||
@ -19,6 +22,14 @@ export function createProjectService(): ProjectService {
|
||||
);
|
||||
}
|
||||
|
||||
export function createNotificationService(): NotificationService {
|
||||
return new NotificationService(new NotificationRepository(getDb()));
|
||||
}
|
||||
|
||||
export function createActivityLogService(): ActivityLogService {
|
||||
return new ActivityLogService(new ActivityLogRepository(getDb()));
|
||||
}
|
||||
|
||||
export function createUserRepository(): UserRepository {
|
||||
return new UserRepository(getDb());
|
||||
}
|
||||
|
||||
@ -239,3 +239,56 @@ export interface SchemaMigration {
|
||||
filename: string;
|
||||
appliedAt: string;
|
||||
}
|
||||
|
||||
// ===== 通知イベント =====
|
||||
|
||||
export interface NotificationEventBase {
|
||||
projectId: number | null;
|
||||
title: string;
|
||||
body: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知イベントの判別共用体。
|
||||
* resolveTargets で type ごとに対象ユーザーを解決する。
|
||||
*/
|
||||
export type NotificationEvent =
|
||||
| (NotificationEventBase & {
|
||||
type: 'mention';
|
||||
mentionedUserId: number;
|
||||
})
|
||||
| (NotificationEventBase & { type: 'todo_assigned'; assigneeId: number })
|
||||
| (NotificationEventBase & { type: 'todo_due_soon'; assigneeId: number })
|
||||
| (NotificationEventBase & {
|
||||
type: 'meeting_invited';
|
||||
memberIds: number[];
|
||||
})
|
||||
| (NotificationEventBase & {
|
||||
type: 'board_commented';
|
||||
threadAuthorId: number;
|
||||
})
|
||||
| (NotificationEventBase & { type: 'project_added'; addedUserId: number })
|
||||
| (NotificationEventBase & {
|
||||
type: 'file_shared';
|
||||
projectMemberIds: number[];
|
||||
})
|
||||
| (NotificationEventBase & {
|
||||
type: 'note_updated';
|
||||
projectMemberIds: number[];
|
||||
});
|
||||
|
||||
/** SSE配信イベント(M8でSseHubが扱う)。M5ではbroadcasterの型として使用 */
|
||||
export type SseEvent =
|
||||
| { type: 'notification.created'; data: { projectId: number | null } }
|
||||
| { type: 'chat.message.created'; data: { projectId: number } }
|
||||
| { type: 'chat.message.updated'; data: { projectId: number } }
|
||||
| { type: 'chat.message.deleted'; data: { projectId: number; id: number } }
|
||||
| { type: 'todo.updated'; data: { projectId: number } }
|
||||
| { type: 'file.uploaded'; data: { projectId: number } }
|
||||
| { type: 'meeting.created'; data: { projectId: number } }
|
||||
| { type: 'note.updated'; data: { projectId: number } };
|
||||
|
||||
/** SSE配信を行うコンポーネントの抽象(M8のSseHubが実装) */
|
||||
export interface SseBroadcaster {
|
||||
broadcast(projectId: number, event: SseEvent): void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user