feat: 实时消息与管理端运营能力

新增群聊/私信与公共 WebSocket 总线,站长用户内容档案,并将通知与待审角标改为推送驱动;同步精简管理仪表盘并修复主题换肤 DOM 冲突。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-15 22:58:16 +08:00
parent 29db9ae9b5
commit 42c395a864
55 changed files with 7607 additions and 346 deletions

View File

@@ -117,6 +117,7 @@ func (h *Handlers) Register(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
_ = h.Chat.EnsureDefaultMembership(user.ID)
// 自动登录:走与 Login 相同的 token 签发链路
accessToken, refreshToken, loginUser, err := h.Auth.Login(req.Username, req.Password)
if err != nil {
@@ -263,7 +264,7 @@ func meUserBody(user *model.User, boardIDs []uint) gin.H {
func (h *Handlers) Me(c *gin.Context) {
claims := middleware.CurrentUser(c)
if claims == nil {
resp := gin.H{"user": nil, "unread_count": 0}
resp := gin.H{"user": nil, "unread_count": 0, "chat_unread_count": 0}
// OptionalAuth 识别到封禁:HTTP 仍为 200(/me 不阻断页面),
// 但带 banned/code,客户端静默校正时据此强制下线并弹告知
if c.GetBool(middleware.AccountBannedKey) {
@@ -275,17 +276,22 @@ func (h *Handlers) Me(c *gin.Context) {
}
user, err := h.Auth.GetUserByID(claims.ID)
if err != nil {
c.JSON(http.StatusOK, gin.H{"user": nil, "unread_count": 0})
c.JSON(http.StatusOK, gin.H{"user": nil, "unread_count": 0, "chat_unread_count": 0})
return
}
var unread int64
if n, err := h.Notification.UnreadCount(claims.ID); err == nil {
unread = n
}
chatUnread := h.Chat.TotalUnread(claims.ID)
// 板块管理员带出授权板块,供前端菜单/按钮按板块范围渲染
var boardIDs []uint
if user.Role == model.RoleBoardAdmin {
boardIDs, _ = h.Auth.GetUserBoardIDs(claims.ID)
}
c.JSON(http.StatusOK, gin.H{"user": meUserBody(user, boardIDs), "unread_count": unread})
c.JSON(http.StatusOK, gin.H{
"user": meUserBody(user, boardIDs),
"unread_count": unread,
"chat_unread_count": chatUnread,
})
}