Files
EchoChat/backend/go-service/app/constants/notify.go
bujinyuan f1853f125d feat: Phase 2e-1 统一通知中心 + 我的 TabBar 聚合未读红点
后端(notify 模块)
- 新增 notify 模块:DAO/Service/Pusher 接口/Controller/Router/CleanupTask
- 数据库 DDL:notify_notifications 表 + 3 索引(user+created/user+is_read/user+category)
- 11 种 type 枚举(好友/群聊 9 种 + meeting_* 2 种预留)+ 4 种 category
- 跨模块集成:contact 3 处 Pusher(friend_request/accepted/rejected)
- 跨模块集成:group 6 处 Pusher(invite/join_request/approved/rejected/kicked/role_changed)
- WS handler 断线补偿:连接建立即推送 notify.unread.total
- 5 REST API(4 用户 + 1 管理员广播)+ 2 WS 事件(notify.new / notify.unread.total)
- 30 天已读通知定时清理(未读永久保留)
- Provider/Wire 依赖注入(NotifyPusher、NotifyConnectHook、UserInfoResolver 接口)

前端
- 新增 notify 模块:API/Pinia Store(5 分类分页缓存 + 未读数 + WS 事件)/NotifyItem/通知中心主页
- profile 入口:铃铛 badge + 菜单项 badge + 数字显示
- App.vue/login 初始化 notifyStore WS 监听;logout 调用 notifyStore.reset() 清缓存
- 清理 contact.js/group.js 中散落 toast 与冗余 notify.friend.request/group.join.request 处理
- CustomTabBar 新增 hasDot() 聚合指示器:我的 Tab 显示纯红点(无数字),
  当前聚合 notifyStore.unreadTotal,未来可扩展「资料待完善/安全提醒/新版本」等

文档
- 新增 Phase 2e 整体路线图 docs/plans/2026-04-20-phase2e-design.md
- 新增 Phase 2e-1 专用设计 docs/plans/2026-04-20-phase2e-1-design.md(§6.4 TabBar 聚合红点)
- 新增 Phase 2e-1 实施计划 docs/plans/2026-04-20-phase2e-1-implementation.plan.md
- 新增 E2E 验证报告 test-report-phase2e-1-notification.md(含 Playwright MCP 2 个现场 Bug 修复记录)
- 更新 docs/progress/CURRENT_STATUS.md、docs/api/README.md、docs/api/frontend/notify.md
- 更新 .cursor/rules/project-context.mdc、docs/plans/2026-02-27-echochat-system-design.md

其他
- .gitignore 排除 .playwright-mcp/ MCP 临时快照

架构决策
- 单端 WS 连接:沿用现有 ws.Hub,多端已读同步推迟到 Phase 2f/二期
- 跨模块依赖:contact/group → notify 严格单向(接口注入模式)
- 降级策略:Pusher 先入库后推送;WS 失败不回滚入库;入库失败仅 Warn 不影响业务

Playwright MCP 回归(4 类场景全通)
- 实时推送(admin 广播 → 1s 内前端自动插入 + 角标 +1)
- Deep-link 跳转(好友申请通知 → contact/request 页)
- 批量清零(全部已读按钮)
- TabBar 聚合红点(有未读亮/全部已读灭)与 notifyStore.unreadTotal 三层同步

Made-with: Cursor
2026-04-21 10:21:01 +08:00

106 lines
4.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package constants
// 通知类型枚举notify_notifications.type
// 覆盖好友 / 群聊 / 会议 / 系统四大类,其中 meeting_* 在 Phase 2e-1 仅预留枚举,由 Phase 2e-2/2e-3 业务接入
const (
// 好友相关contact 模块触发)
NotifyTypeFriendRequest = "friend_request" // 收到好友申请
NotifyTypeFriendAccepted = "friend_accepted" // 好友申请被接受(通知申请人)
NotifyTypeFriendRejected = "friend_rejected" // 好友申请被拒绝(通知申请人)
// 群聊相关group 模块触发)
NotifyTypeGroupInvite = "group_invite" // 被邀请入群
NotifyTypeGroupJoinRequest = "group_join_request" // 有新入群申请(通知群管理员)
NotifyTypeGroupJoinApproved = "group_join_approved" // 入群申请通过(通知申请人)
NotifyTypeGroupJoinRejected = "group_join_rejected" // 入群申请被拒(通知申请人)
NotifyTypeGroupKicked = "group_kicked" // 被移出群聊
NotifyTypeGroupRoleChanged = "group_role_changed" // 群内角色变更(被设/取消管理员)
// 系统广播admin 触发)
NotifyTypeSystemBroadcast = "system_broadcast" // 管理员全员广播
// 会议相关Phase 2e-1 预留枚举Phase 2e-2/2e-3 业务接入)
NotifyTypeMeetingInvite = "meeting_invite" // 会议邀请
NotifyTypeMeetingReminder = "meeting_reminder" // 会议提醒
)
// NotifyTypeMap 通知类型中文映射
// 用于日志、管理端展示、默认标题兜底
var NotifyTypeMap = map[string]string{
NotifyTypeFriendRequest: "好友申请",
NotifyTypeFriendAccepted: "好友申请通过",
NotifyTypeFriendRejected: "好友申请被拒",
NotifyTypeGroupInvite: "群聊邀请",
NotifyTypeGroupJoinRequest: "入群申请",
NotifyTypeGroupJoinApproved: "入群申请通过",
NotifyTypeGroupJoinRejected: "入群申请被拒",
NotifyTypeGroupKicked: "移出群聊",
NotifyTypeGroupRoleChanged: "群角色变更",
NotifyTypeSystemBroadcast: "系统通知",
NotifyTypeMeetingInvite: "会议邀请",
NotifyTypeMeetingReminder: "会议提醒",
}
// 通知分类(用于前端 5 个 Tab 过滤)
const (
NotifyCategoryAll = "all" // 全部
NotifyCategoryFriend = "friend" // 好友
NotifyCategoryGroup = "group" // 群聊
NotifyCategoryMeeting = "meeting" // 会议
NotifyCategorySystem = "system" // 系统
)
// NotifyCategoryOfType 根据 type 判断所属分类,用于列表按分类筛选
func NotifyCategoryOfType(notifyType string) string {
switch notifyType {
case NotifyTypeFriendRequest, NotifyTypeFriendAccepted, NotifyTypeFriendRejected:
return NotifyCategoryFriend
case NotifyTypeGroupInvite, NotifyTypeGroupJoinRequest, NotifyTypeGroupJoinApproved,
NotifyTypeGroupJoinRejected, NotifyTypeGroupKicked, NotifyTypeGroupRoleChanged:
return NotifyCategoryGroup
case NotifyTypeMeetingInvite, NotifyTypeMeetingReminder:
return NotifyCategoryMeeting
case NotifyTypeSystemBroadcast:
return NotifyCategorySystem
default:
return NotifyCategorySystem
}
}
// NotifyTypesByCategory 分类 → type 列表映射,查询时用于构造 SQL IN 条件
var NotifyTypesByCategory = map[string][]string{
NotifyCategoryFriend: {
NotifyTypeFriendRequest, NotifyTypeFriendAccepted, NotifyTypeFriendRejected,
},
NotifyCategoryGroup: {
NotifyTypeGroupInvite, NotifyTypeGroupJoinRequest, NotifyTypeGroupJoinApproved,
NotifyTypeGroupJoinRejected, NotifyTypeGroupKicked, NotifyTypeGroupRoleChanged,
},
NotifyCategoryMeeting: {
NotifyTypeMeetingInvite, NotifyTypeMeetingReminder,
},
NotifyCategorySystem: {
NotifyTypeSystemBroadcast,
},
}
// 通知业务对象类型notify_notifications.target_type
// 用于前端 deep-link 跳转路由
const (
NotifyTargetUser = "user" // 跳转到用户主页/好友申请页
NotifyTargetGroup = "group" // 跳转到群聊详情
NotifyTargetMeeting = "meeting" // 跳转到会议Phase 2e-2/2e-3
NotifyTargetSystem = "system" // 系统公告,无跳转
)
// 通知清理策略
const (
NotifyRetentionDays = 30 // 已读通知保留天数(未读不清理)
)
// WS 事件常量
const (
WSEventNotifyNew = "notify.new" // 新通知推送S→C
WSEventNotifyUnreadTotal = "notify.unread.total" // 断线重连补偿未读数S→C
)