2.3 KiB
2.3 KiB
Context
已有实体/资源/组织等业务域均为「BaseCrudService + 软删 + 租户隔离 + 资源表授权」的同构结构。通知无长连接需求,用轮询即可。动机见 proposal.md - Why。
Goals / Non-Goals
Goals:
- 管理员发布公告,本租户用户经顶栏铃铛查看未读、标记已读;未读数轮询刷新。
Non-Goals:
- 不做定向通知(按用户/角色投递),本期仅广播式公告。
- 不接邮件/短信,不做 WebSocket 实时推送。
Decisions
D1 — 实体:Notice + NoticeRead
Notice(sys_notice):id、tenant_code、title(128)、content(text)、status(enabled/disabled)、created_by、created_at/updated_at/deleted_at;软删,无业务唯一键(无需改名回收)。NoticeRead(sys_notice_read):tenant_code、notice_id、user_id、read_at,唯一(tenant_code, notice_id, user_id);不软删(追踪实体)。
D2 — 广播式 + enabled 才可见
通知对本租户所有用户可见,status=enabled 才计入未读/列表;disabled 仅管理员列表可见。未读数 = enabled 通知数 − 当前用户已读数(两条 count 相减)。
D3 — 用户侧接口仅登录态,管理侧挂权限
- 用户侧
GET /notices/mine、GET /notices/unread-count、POST /notices/:id/read、POST /notices/read-all不加@RequirePermissions(仅需登录)。 - 管理侧
GET/POST/PATCH/DELETE /notices挂notice:list/create/update/delete+ 写操作@AuditLog('notice', ...)。
D4 — 轮询未读数
前端 notice store 启动 30s 定时器轮询 unread-count(登录后启动、登出/401 停止),路由切换与标记已读后即时刷新;铃铛用 el-badge 显示未读数、el-popover 展示最近通知列表。
Risks / Trade-offs
- [轮询有延迟] → 30s 内新通知才被看到;站内公告场景可接受,标记已读后即时刷新兜底。
- [未读数计算随通知量增长] → 用
count聚合(有(tenant_code, notice_id, user_id)索引),不拉全量。
Migration Plan
- Prisma migration:新建
sys_notice、sys_notice_read(无回填)。 - 代码上线 +
pnpm sync:resources补齐 notice 菜单/接口。 - 回滚:删除两张表 + 回滚代码即可。
Open Questions
无。