feat(message): add message center module (Phase 5)

Implement the complete message center with:
- Database migrations for message_templates, messages, message_subscriptions tables
- erp-message crate with entities, DTOs, services, handlers
- Message CRUD, send, read/unread tracking, soft delete
- Template management with variable interpolation
- Subscription preferences with DND support
- Frontend: messages page, notification panel, unread count badge
- Server integration with module registration and routing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-04-11 12:25:05 +08:00
parent 91ecaa3ed7
commit 5ceed71e62
35 changed files with 2252 additions and 15 deletions

View File

@@ -0,0 +1,47 @@
import { create } from 'zustand';
import { getUnreadCount, listMessages, markRead, type MessageInfo } from '../api/messages';
interface MessageState {
unreadCount: number;
recentMessages: MessageInfo[];
fetchUnreadCount: () => Promise<void>;
fetchRecentMessages: () => Promise<void>;
markAsRead: (id: string) => Promise<void>;
}
export const useMessageStore = create<MessageState>((set) => ({
unreadCount: 0,
recentMessages: [],
fetchUnreadCount: async () => {
try {
const result = await getUnreadCount();
set({ unreadCount: result.count });
} catch {
// 静默失败,不影响用户体验
}
},
fetchRecentMessages: async () => {
try {
const result = await listMessages({ page: 1, page_size: 5 });
set({ recentMessages: result.data });
} catch {
// 静默失败
}
},
markAsRead: async (id: string) => {
try {
await markRead(id);
set((state) => ({
unreadCount: Math.max(0, state.unreadCount - 1),
recentMessages: state.recentMessages.map((m) =>
m.id === id ? { ...m, is_read: true } : m,
),
}));
} catch {
// 静默失败
}
},
}));