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:
@ -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