feat(notification): 新增全部标记已读接口
This commit is contained in:
@@ -60,6 +60,22 @@ func (d *NotificationDao) NotificationUpdateReadByUUIDIfUnread(ctx context.Conte
|
||||
return res.RowsAffected > 0, nil
|
||||
}
|
||||
|
||||
// NotificationUpdateReadAllByUserID 将该用户全部未读通知标记为已读,返回影响行数。
|
||||
// 条件带 is_read = 1,保证只统计本次真正从未读变为已读的条数(幂等,重复调用返回 0)。
|
||||
func (d *NotificationDao) NotificationUpdateReadAllByUserID(ctx context.Context, userID int64) (int64, error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "mysqldao.NotificationUpdateReadAllByUserID")
|
||||
defer span.End()
|
||||
|
||||
res := d.db.WithContext(ctx).Model(&mysqlmodel.UserNotification{}).
|
||||
Where("user_id = ? AND is_read = ?", userID, int8(1)).
|
||||
Update("is_read", int8(2))
|
||||
if res.Error != nil {
|
||||
span.RecordError(res.Error)
|
||||
return 0, res.Error
|
||||
}
|
||||
return res.RowsAffected, nil
|
||||
}
|
||||
|
||||
// NotificationGetByUUID 按 UUID 查询通知
|
||||
func (d *NotificationDao) NotificationGetByUUID(ctx context.Context, notificationUUID string) (*mysqlmodel.UserNotification, error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "mysqldao.NotificationGetByUUID")
|
||||
|
||||
@@ -77,6 +77,22 @@ func (h *NotificationHandler) Get(c *gin.Context) {
|
||||
response.ToResponse(out)
|
||||
}
|
||||
|
||||
// ReadAll 将当前用户全部通知标记为已读(前端进入通知页时调用)。
|
||||
func (h *NotificationHandler) ReadAll(c *gin.Context) {
|
||||
response := app.NewResponse(c)
|
||||
userID, ok := middleware.GetUserID(c.Request.Context())
|
||||
if !ok {
|
||||
response.ToErrorResponse(errcode.Unauthorized)
|
||||
return
|
||||
}
|
||||
out, e := h.svc.ReadAll(c.Request.Context(), userID)
|
||||
if e != nil {
|
||||
response.ToErrorResponse(e)
|
||||
return
|
||||
}
|
||||
response.ToResponse(out)
|
||||
}
|
||||
|
||||
// Read 标记通知已读(uuid 来自路径参数)。
|
||||
func (h *NotificationHandler) Read(c *gin.Context) {
|
||||
response := app.NewResponse(c)
|
||||
|
||||
@@ -121,6 +121,8 @@ func NewRouter() *gin.Engine {
|
||||
// 通知
|
||||
auth.GET("/notification", notificationHandler.List)
|
||||
auth.GET("/notification/unread", notificationHandler.Unread)
|
||||
// 静态路径 /notification/read-all 与 /notification/:uuid/read 段数不同,不会互相抢占
|
||||
auth.POST("/notification/read-all", notificationHandler.ReadAll)
|
||||
auth.GET("/notification/:uuid", notificationHandler.Get)
|
||||
auth.POST("/notification/:uuid/read", notificationHandler.Read)
|
||||
|
||||
|
||||
@@ -108,6 +108,26 @@ func (s *NotificationService) Read(ctx context.Context, userID int64, request *d
|
||||
return &dto.NotificationUnreadResponse{UnreadCount: cnt}, nil
|
||||
}
|
||||
|
||||
// ReadAll 将当前用户全部未读通知标记为已读,并回填未读数缓存。
|
||||
// 与单条已读一致:以 MySQL 为权威重新统计后返回,避免 Redis 计数偏差。
|
||||
func (s *NotificationService) ReadAll(ctx context.Context, userID int64) (*dto.NotificationUnreadResponse, *errcode.Error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "svc.NotificationReadAll")
|
||||
defer span.End()
|
||||
|
||||
affected, err := s.notificationDao.NotificationUpdateReadAllByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "mark all notifications read failed", "user_id", userID, "error", err)
|
||||
return nil, errcode.DatabaseError
|
||||
}
|
||||
slog.InfoContext(ctx, "mark all notifications read", "user_id", userID, "affected", affected)
|
||||
|
||||
cnt, cerr := s.unreadCount(ctx, userID)
|
||||
if cerr != nil {
|
||||
return nil, cerr
|
||||
}
|
||||
return &dto.NotificationUnreadResponse{UnreadCount: cnt}, nil
|
||||
}
|
||||
|
||||
// Unread 获取当前用户未读数(以 MySQL 为权威)。
|
||||
func (s *NotificationService) Unread(ctx context.Context, userID int64) (*dto.NotificationUnreadResponse, *errcode.Error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "svc.NotificationUnread")
|
||||
|
||||
Reference in New Issue
Block a user