1use serde::{Deserialize, Serialize};
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
24#[serde(tag = "type", content = "data")]
25pub enum NotificationType {
26 FriendRequestSent {
29 request_id: u64,
30 from_user_id: u64,
31 to_user_id: u64,
32 message: String,
33 },
34
35 FriendRequestAccepted {
37 request_id: u64,
38 user_id: u64,
39 username: String,
40 avatar: Option<String>,
41 },
42
43 FriendRequestRejected { request_id: u64, user_id: u64 },
45
46 FriendDeleted { user_id: u64, username: String },
48
49 GroupCreated {
52 group_id: u64,
53 group_name: String,
54 creator_id: u64,
55 creator_name: String,
56 member_count: u32,
57 },
58
59 GroupMemberJoined {
61 group_id: u64,
62 group_name: String,
63 user_id: u64,
64 username: String,
65 invited_by: Option<u64>,
66 inviter_name: Option<String>,
67 },
68
69 GroupMemberLeft {
71 group_id: u64,
72 group_name: String,
73 user_id: u64,
74 username: String,
75 },
76
77 GroupMemberKicked {
79 group_id: u64,
80 group_name: String,
81 user_id: u64,
82 username: String,
83 kicked_by: u64,
84 kicker_name: String,
85 reason: Option<String>,
86 },
87
88 GroupNameChanged {
90 group_id: u64,
91 old_name: String,
92 new_name: String,
93 changed_by: u64,
94 changer_name: String,
95 },
96
97 GroupAvatarChanged {
99 group_id: u64,
100 group_name: String,
101 changed_by: u64,
102 changer_name: String,
103 new_avatar_url: String,
104 },
105
106 GroupAnnouncementChanged {
108 group_id: u64,
109 group_name: String,
110 announcement: String,
111 changed_by: u64,
112 changer_name: String,
113 },
114
115 GroupOwnerTransferred {
117 group_id: u64,
118 group_name: String,
119 old_owner_id: u64,
120 old_owner_name: String,
121 new_owner_id: u64,
122 new_owner_name: String,
123 },
124
125 GroupAdminAdded {
127 group_id: u64,
128 group_name: String,
129 user_id: u64,
130 username: String,
131 added_by: u64,
132 adder_name: String,
133 },
134
135 GroupAdminRemoved {
137 group_id: u64,
138 group_name: String,
139 user_id: u64,
140 username: String,
141 removed_by: u64,
142 remover_name: String,
143 },
144
145 GroupMemberMuted {
147 group_id: u64,
148 group_name: String,
149 user_id: u64,
150 username: String,
151 duration_seconds: u64,
152 muted_by: u64,
153 muter_name: String,
154 reason: Option<String>,
155 },
156
157 GroupMemberUnmuted {
159 group_id: u64,
160 group_name: String,
161 user_id: u64,
162 username: String,
163 unmuted_by: u64,
164 unmuter_name: String,
165 },
166
167 GroupDismissed {
169 group_id: u64,
170 group_name: String,
171 dismissed_by: u64,
172 dismisser_name: String,
173 },
174
175 RedPacketSent {
178 red_packet_id: String,
179 from_user_id: u64,
180 from_username: String,
181 total_amount: i64, count: u32, message: String, red_packet_type: RedPacketType,
185 },
186
187 RedPacketReceived {
189 red_packet_id: String,
190 user_id: u64,
191 username: String,
192 amount: i64, timestamp: i64,
194 },
195
196 RedPacketEmpty {
198 red_packet_id: String,
199 total_received: u32, total_amount: i64, },
202
203 RedPacketExpired {
205 red_packet_id: String,
206 remaining_count: u32, remaining_amount: i64, },
209
210 MessageRevoked {
213 server_message_id: u64,
214 channel_id: u64,
215 revoked_by: u64,
216 revoker_name: String,
217 revoked_at: i64,
218 },
219
220 MessagePinned {
222 server_message_id: u64,
223 channel_id: u64,
224 pinned_by: u64,
225 pinner_name: String,
226 pinned_at: i64,
227 },
228
229 MessageUnpinned {
231 server_message_id: u64,
232 channel_id: u64,
233 unpinned_by: u64,
234 unpinner_name: String,
235 unpinned_at: i64,
236 },
237
238 MessageRead {
240 server_message_id: u64,
241 channel_id: u64,
242 reader_id: u64,
243 reader_name: String,
244 read_at: i64,
245 },
246
247 MessageEdited {
249 server_message_id: u64,
250 channel_id: u64,
251 editor_id: u64,
252 editor_name: String,
253 old_content: String,
254 new_content: String,
255 edited_at: i64,
256 },
257
258 SystemMaintenance {
261 title: String,
262 content: String,
263 start_time: i64,
264 end_time: i64,
265 level: MaintenanceLevel,
266 },
267
268 SystemAnnouncement {
270 announcement_id: u64,
271 title: String,
272 content: String,
273 level: AnnouncementLevel,
274 published_at: i64,
275 },
276
277 SystemVersionUpdate {
279 version: String,
280 description: String,
281 update_url: String,
282 force_update: bool,
283 },
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
288pub enum RedPacketType {
289 Normal,
291 Lucky,
293 Exclusive { target_user_ids: Vec<u64> },
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
299pub enum MaintenanceLevel {
300 Normal,
302 Urgent,
304 Scheduled,
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize)]
310pub enum AnnouncementLevel {
311 Info,
313 Warning,
315 Important,
317 Critical,
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize)]
325pub struct NotificationMessage {
326 pub notification_id: Option<u64>,
328
329 pub notification_type: NotificationType,
331
332 pub display_text: String,
334
335 pub timestamp: i64,
337
338 pub channel_id: u64,
340
341 pub channel_type: u8,
343
344 pub should_persist: bool,
346
347 pub metadata: Option<serde_json::Value>,
349}
350
351impl NotificationMessage {
352 pub fn new(
354 notification_type: NotificationType,
355 display_text: String,
356 channel_id: u64,
357 channel_type: u8,
358 ) -> Self {
359 Self {
360 notification_id: None,
361 notification_type,
362 display_text,
363 timestamp: chrono::Utc::now().timestamp(),
364 channel_id,
365 channel_type,
366 should_persist: true,
367 metadata: None,
368 }
369 }
370
371 pub fn with_notification_id(mut self, id: u64) -> Self {
373 self.notification_id = Some(id);
374 self
375 }
376
377 pub fn with_persist(mut self, persist: bool) -> Self {
379 self.should_persist = persist;
380 self
381 }
382
383 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
385 self.metadata = Some(metadata);
386 self
387 }
388
389 pub fn type_str(&self) -> &'static str {
391 match &self.notification_type {
392 NotificationType::FriendRequestSent { .. } => "friend_request_sent",
393 NotificationType::FriendRequestAccepted { .. } => "friend_request_accepted",
394 NotificationType::FriendRequestRejected { .. } => "friend_request_rejected",
395 NotificationType::FriendDeleted { .. } => "friend_deleted",
396 NotificationType::GroupCreated { .. } => "group_created",
397 NotificationType::GroupMemberJoined { .. } => "group_member_joined",
398 NotificationType::GroupMemberLeft { .. } => "group_member_left",
399 NotificationType::GroupMemberKicked { .. } => "group_member_kicked",
400 NotificationType::GroupNameChanged { .. } => "group_name_changed",
401 NotificationType::GroupAvatarChanged { .. } => "group_avatar_changed",
402 NotificationType::GroupAnnouncementChanged { .. } => "group_announcement_changed",
403 NotificationType::GroupOwnerTransferred { .. } => "group_owner_transferred",
404 NotificationType::GroupAdminAdded { .. } => "group_admin_added",
405 NotificationType::GroupAdminRemoved { .. } => "group_admin_removed",
406 NotificationType::GroupMemberMuted { .. } => "group_member_muted",
407 NotificationType::GroupMemberUnmuted { .. } => "group_member_unmuted",
408 NotificationType::GroupDismissed { .. } => "group_dismissed",
409 NotificationType::RedPacketSent { .. } => "red_packet_sent",
410 NotificationType::RedPacketReceived { .. } => "red_packet_received",
411 NotificationType::RedPacketEmpty { .. } => "red_packet_empty",
412 NotificationType::RedPacketExpired { .. } => "red_packet_expired",
413 NotificationType::MessageRevoked { .. } => "message_revoked",
414 NotificationType::MessagePinned { .. } => "message_pinned",
415 NotificationType::MessageUnpinned { .. } => "message_unpinned",
416 NotificationType::MessageRead { .. } => "message_read",
417 NotificationType::MessageEdited { .. } => "message_edited",
418 NotificationType::SystemMaintenance { .. } => "system_maintenance",
419 NotificationType::SystemAnnouncement { .. } => "system_announcement",
420 NotificationType::SystemVersionUpdate { .. } => "system_version_update",
421 }
422 }
423}
424
425impl NotificationMessage {
427 pub fn generate_display_text_cn(notification_type: &NotificationType) -> String {
429 match notification_type {
430 NotificationType::FriendRequestAccepted { username, .. } => {
431 format!("{} 接受了你的好友请求", username)
432 }
433 NotificationType::FriendDeleted { username, .. } => {
434 format!("{} 删除了你的好友关系", username)
435 }
436 NotificationType::GroupMemberJoined {
437 username,
438 inviter_name,
439 ..
440 } => {
441 if let Some(inviter) = inviter_name {
442 format!("{} 邀请 {} 加入了群聊", inviter, username)
443 } else {
444 format!("{} 加入了群聊", username)
445 }
446 }
447 NotificationType::GroupMemberLeft { username, .. } => {
448 format!("{} 离开了群聊", username)
449 }
450 NotificationType::GroupMemberKicked {
451 username,
452 kicker_name,
453 reason,
454 ..
455 } => {
456 if let Some(r) = reason {
457 format!("{} 将 {} 移出了群聊(原因:{})", kicker_name, username, r)
458 } else {
459 format!("{} 将 {} 移出了群聊", kicker_name, username)
460 }
461 }
462 NotificationType::GroupNameChanged {
463 old_name,
464 new_name,
465 changer_name,
466 ..
467 } => {
468 format!(
469 "{} 将群名称从「{}」改为「{}」",
470 changer_name, old_name, new_name
471 )
472 }
473 NotificationType::GroupOwnerTransferred {
474 old_owner_name,
475 new_owner_name,
476 ..
477 } => {
478 format!("{} 将群主转让给 {}", old_owner_name, new_owner_name)
479 }
480 NotificationType::GroupAdminAdded {
481 username,
482 adder_name,
483 ..
484 } => {
485 format!("{} 将 {} 设置为管理员", adder_name, username)
486 }
487 NotificationType::GroupMemberMuted {
488 username,
489 muter_name,
490 duration_seconds,
491 ..
492 } => {
493 let duration_text = format_duration(*duration_seconds);
494 format!("{} 禁言了 {}({})", muter_name, username, duration_text)
495 }
496 NotificationType::RedPacketSent {
497 from_username,
498 message,
499 ..
500 } => {
501 format!("{} 发送了红包「{}」", from_username, message)
502 }
503 NotificationType::RedPacketReceived {
504 username, amount, ..
505 } => {
506 format!("{} 领取了红包({:.2}元)", username, *amount as f64 / 100.0)
507 }
508 NotificationType::RedPacketEmpty { .. } => "红包已被抢完".to_string(),
509 NotificationType::MessageRevoked { revoker_name, .. } => {
510 format!("{} 撤回了一条消息", revoker_name)
511 }
512 NotificationType::MessagePinned { pinner_name, .. } => {
513 format!("{} 置顶了一条消息", pinner_name)
514 }
515 NotificationType::MessageRead { reader_name, .. } => {
516 format!("{} 已读", reader_name)
517 }
518 NotificationType::SystemMaintenance { title, .. } => {
519 format!("系统维护通知:{}", title)
520 }
521 NotificationType::SystemAnnouncement { title, .. } => {
522 format!("系统公告:{}", title)
523 }
524 _ => "系统通知".to_string(),
525 }
526 }
527}
528
529fn format_duration(seconds: u64) -> String {
531 if seconds < 60 {
532 format!("{}秒", seconds)
533 } else if seconds < 3600 {
534 format!("{}分钟", seconds / 60)
535 } else if seconds < 86400 {
536 format!("{}小时", seconds / 3600)
537 } else {
538 format!("{}天", seconds / 86400)
539 }
540}
541
542#[cfg(test)]
543mod tests {
544 use super::*;
545
546 #[test]
547 fn test_notification_serialization() {
548 let notification = NotificationType::GroupMemberJoined {
549 group_id: 123,
550 group_name: "测试群".to_string(),
551 user_id: 456,
552 username: "张三".to_string(),
553 invited_by: Some(789),
554 inviter_name: Some("李四".to_string()),
555 };
556
557 let json = serde_json::to_string(¬ification).unwrap();
558 assert!(json.contains("group_member_joined"));
559 assert!(json.contains("张三"));
560
561 let deserialized: NotificationType = serde_json::from_str(&json).unwrap();
562 assert!(matches!(
563 deserialized,
564 NotificationType::GroupMemberJoined { .. }
565 ));
566 }
567
568 #[test]
569 fn test_display_text_generation() {
570 let notification = NotificationType::GroupMemberJoined {
571 group_id: 123,
572 group_name: "测试群".to_string(),
573 user_id: 456,
574 username: "张三".to_string(),
575 invited_by: Some(789),
576 inviter_name: Some("李四".to_string()),
577 };
578
579 let text = NotificationMessage::generate_display_text_cn(¬ification);
580 assert_eq!(text, "李四 邀请 张三 加入了群聊");
581 }
582
583 #[test]
584 fn test_notification_message_builder() {
585 let notification_type = NotificationType::FriendRequestAccepted {
586 request_id: 1,
587 user_id: 123,
588 username: "Alice".to_string(),
589 avatar: None,
590 };
591
592 let msg = NotificationMessage::new(
593 notification_type.clone(),
594 "Alice 接受了你的好友请求".to_string(),
595 456,
596 1,
597 )
598 .with_notification_id(789)
599 .with_persist(true);
600
601 assert_eq!(msg.notification_id, Some(789));
602 assert_eq!(msg.channel_id, 456);
603 assert!(msg.should_persist);
604 }
605
606 #[test]
607 fn test_format_duration() {
608 assert_eq!(format_duration(30), "30秒");
609 assert_eq!(format_duration(120), "2分钟");
610 assert_eq!(format_duration(3600), "1小时");
611 assert_eq!(format_duration(86400), "1天");
612 }
613}