2026-03-09 21:28:58 +08:00
|
|
|
package repository
|
|
|
|
|
|
|
|
|
|
import (
|
2026-03-21 03:21:07 +08:00
|
|
|
"context"
|
|
|
|
|
"strconv"
|
|
|
|
|
|
2026-03-09 21:28:58 +08:00
|
|
|
"carrot_bbs/internal/model"
|
2026-03-21 03:21:07 +08:00
|
|
|
"carrot_bbs/internal/pkg/cursor"
|
2026-03-09 21:28:58 +08:00
|
|
|
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-26 18:14:16 +08:00
|
|
|
// SystemNotificationRepository 系统通知仓储接口
|
|
|
|
|
type SystemNotificationRepository interface {
|
|
|
|
|
Create(notification *model.SystemNotification) error
|
|
|
|
|
GetByID(id int64) (*model.SystemNotification, error)
|
|
|
|
|
GetByReceiverID(receiverID string, page, pageSize int) ([]*model.SystemNotification, int64, error)
|
|
|
|
|
GetUnreadByReceiverID(receiverID string, limit int) ([]*model.SystemNotification, error)
|
|
|
|
|
GetUnreadCount(receiverID string) (int64, error)
|
|
|
|
|
MarkAsRead(id int64, receiverID string) error
|
|
|
|
|
MarkAllAsRead(receiverID string) error
|
|
|
|
|
Delete(id int64, receiverID string) error
|
|
|
|
|
GetByType(receiverID string, notifyType model.SystemNotificationType, page, pageSize int) ([]*model.SystemNotification, int64, error)
|
|
|
|
|
GetByReceiverIDCursor(ctx context.Context, receiverID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.SystemNotification], error)
|
|
|
|
|
GetByReceiverIDCursorLegacy(receiverID string, cursorStr string, pageSize int) ([]*model.SystemNotification, string, bool, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// systemNotificationRepository 系统通知仓储实现
|
|
|
|
|
type systemNotificationRepository struct {
|
2026-03-09 21:28:58 +08:00
|
|
|
db *gorm.DB
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewSystemNotificationRepository 创建系统通知仓储
|
2026-03-26 18:14:16 +08:00
|
|
|
func NewSystemNotificationRepository(db *gorm.DB) SystemNotificationRepository {
|
|
|
|
|
return &systemNotificationRepository{db: db}
|
2026-03-09 21:28:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create 创建系统通知
|
2026-03-26 18:14:16 +08:00
|
|
|
func (r *systemNotificationRepository) Create(notification *model.SystemNotification) error {
|
2026-03-09 21:28:58 +08:00
|
|
|
return r.db.Create(notification).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetByID 根据ID获取通知
|
2026-03-26 18:14:16 +08:00
|
|
|
func (r *systemNotificationRepository) GetByID(id int64) (*model.SystemNotification, error) {
|
2026-03-09 21:28:58 +08:00
|
|
|
var notification model.SystemNotification
|
|
|
|
|
err := r.db.First(¬ification, "id = ?", id).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return ¬ification, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetByReceiverID 获取用户的通知列表
|
2026-03-26 18:14:16 +08:00
|
|
|
func (r *systemNotificationRepository) GetByReceiverID(receiverID string, page, pageSize int) ([]*model.SystemNotification, int64, error) {
|
2026-03-09 21:28:58 +08:00
|
|
|
var notifications []*model.SystemNotification
|
|
|
|
|
var total int64
|
|
|
|
|
|
|
|
|
|
query := r.db.Model(&model.SystemNotification{}).Where("receiver_id = ?", receiverID)
|
|
|
|
|
query.Count(&total)
|
|
|
|
|
|
|
|
|
|
offset := (page - 1) * pageSize
|
|
|
|
|
err := query.Offset(offset).
|
|
|
|
|
Limit(pageSize).
|
|
|
|
|
Order("created_at DESC").
|
|
|
|
|
Find(¬ifications).Error
|
|
|
|
|
|
|
|
|
|
return notifications, total, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetUnreadByReceiverID 获取用户的未读通知列表
|
2026-03-26 18:14:16 +08:00
|
|
|
func (r *systemNotificationRepository) GetUnreadByReceiverID(receiverID string, limit int) ([]*model.SystemNotification, error) {
|
2026-03-09 21:28:58 +08:00
|
|
|
var notifications []*model.SystemNotification
|
|
|
|
|
err := r.db.Where("receiver_id = ? AND is_read = ?", receiverID, false).
|
|
|
|
|
Order("created_at DESC").
|
|
|
|
|
Limit(limit).
|
|
|
|
|
Find(¬ifications).Error
|
|
|
|
|
return notifications, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetUnreadCount 获取用户未读通知数量
|
2026-03-26 18:14:16 +08:00
|
|
|
func (r *systemNotificationRepository) GetUnreadCount(receiverID string) (int64, error) {
|
2026-03-09 21:28:58 +08:00
|
|
|
var count int64
|
|
|
|
|
err := r.db.Model(&model.SystemNotification{}).
|
|
|
|
|
Where("receiver_id = ? AND is_read = ?", receiverID, false).
|
|
|
|
|
Count(&count).Error
|
|
|
|
|
return count, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MarkAsRead 标记单条通知为已读
|
2026-03-26 18:14:16 +08:00
|
|
|
func (r *systemNotificationRepository) MarkAsRead(id int64, receiverID string) error {
|
2026-03-09 21:28:58 +08:00
|
|
|
now := model.SystemNotification{}.UpdatedAt
|
|
|
|
|
return r.db.Model(&model.SystemNotification{}).
|
|
|
|
|
Where("id = ? AND receiver_id = ?", id, receiverID).
|
2026-03-30 04:49:35 +08:00
|
|
|
Updates(map[string]any{
|
2026-03-09 21:28:58 +08:00
|
|
|
"is_read": true,
|
|
|
|
|
"read_at": now,
|
|
|
|
|
}).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MarkAllAsRead 标记用户所有通知为已读
|
2026-03-26 18:14:16 +08:00
|
|
|
func (r *systemNotificationRepository) MarkAllAsRead(receiverID string) error {
|
2026-03-09 21:28:58 +08:00
|
|
|
now := model.SystemNotification{}.UpdatedAt
|
|
|
|
|
return r.db.Model(&model.SystemNotification{}).
|
|
|
|
|
Where("receiver_id = ? AND is_read = ?", receiverID, false).
|
2026-03-30 04:49:35 +08:00
|
|
|
Updates(map[string]any{
|
2026-03-09 21:28:58 +08:00
|
|
|
"is_read": true,
|
|
|
|
|
"read_at": now,
|
|
|
|
|
}).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Delete 删除通知(软删除)
|
2026-03-26 18:14:16 +08:00
|
|
|
func (r *systemNotificationRepository) Delete(id int64, receiverID string) error {
|
2026-03-09 21:28:58 +08:00
|
|
|
return r.db.Where("id = ? AND receiver_id = ?", id, receiverID).
|
|
|
|
|
Delete(&model.SystemNotification{}).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetByType 获取用户指定类型的通知
|
2026-03-26 18:14:16 +08:00
|
|
|
func (r *systemNotificationRepository) GetByType(receiverID string, notifyType model.SystemNotificationType, page, pageSize int) ([]*model.SystemNotification, int64, error) {
|
2026-03-09 21:28:58 +08:00
|
|
|
var notifications []*model.SystemNotification
|
|
|
|
|
var total int64
|
|
|
|
|
|
|
|
|
|
query := r.db.Model(&model.SystemNotification{}).
|
|
|
|
|
Where("receiver_id = ? AND type = ?", receiverID, notifyType)
|
|
|
|
|
query.Count(&total)
|
|
|
|
|
|
|
|
|
|
offset := (page - 1) * pageSize
|
|
|
|
|
err := query.Offset(offset).
|
|
|
|
|
Limit(pageSize).
|
|
|
|
|
Order("created_at DESC").
|
|
|
|
|
Find(¬ifications).Error
|
|
|
|
|
|
|
|
|
|
return notifications, total, err
|
|
|
|
|
}
|
2026-03-21 02:48:06 +08:00
|
|
|
|
|
|
|
|
// GetByReceiverIDCursor 游标分页获取用户的通知列表
|
2026-03-21 03:21:07 +08:00
|
|
|
// 使用统一的 cursor 包进行游标分页
|
2026-03-26 18:14:16 +08:00
|
|
|
func (r *systemNotificationRepository) GetByReceiverIDCursor(ctx context.Context, receiverID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.SystemNotification], error) {
|
2026-03-21 03:21:07 +08:00
|
|
|
// 构建基础查询
|
|
|
|
|
query := r.db.WithContext(ctx).Model(&model.SystemNotification{}).Where("receiver_id = ?", receiverID)
|
|
|
|
|
|
|
|
|
|
// 使用 CursorBuilder 构建游标查询
|
|
|
|
|
builder := cursor.NewBuilder(query, cursor.SortByCreatedAtDesc).
|
|
|
|
|
WithCursor(req.Cursor, cursor.Direction(req.Direction)).
|
|
|
|
|
WithPageSize(req.PageSize)
|
|
|
|
|
|
|
|
|
|
if builder.Error() != nil {
|
|
|
|
|
return nil, builder.Error()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 执行查询
|
|
|
|
|
var notifications []*model.SystemNotification
|
|
|
|
|
if err := builder.Build().Find(¬ifications).Error; err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 构建响应
|
|
|
|
|
pageSize := builder.GetPageSize()
|
|
|
|
|
hasMore := cursor.HasMore(len(notifications), pageSize)
|
|
|
|
|
if hasMore {
|
|
|
|
|
notifications = notifications[:pageSize]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成下一页游标
|
|
|
|
|
var nextCursor string
|
|
|
|
|
if hasMore && len(notifications) > 0 {
|
|
|
|
|
lastNotification := notifications[len(notifications)-1]
|
|
|
|
|
nextCursor = cursor.NewCursor(
|
|
|
|
|
cursor.FormatTime(lastNotification.CreatedAt),
|
|
|
|
|
strconv.FormatInt(lastNotification.ID, 10),
|
|
|
|
|
cursor.SortByCreatedAtDesc,
|
|
|
|
|
).Encode()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &cursor.CursorPageResult[*model.SystemNotification]{
|
|
|
|
|
Items: notifications,
|
|
|
|
|
NextCursor: nextCursor,
|
|
|
|
|
HasMore: hasMore,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetByReceiverIDCursorLegacy 游标分页获取用户的通知列表(旧版兼容接口)
|
|
|
|
|
// 已废弃:请使用 GetByReceiverIDCursor
|
2026-03-26 18:14:16 +08:00
|
|
|
func (r *systemNotificationRepository) GetByReceiverIDCursorLegacy(receiverID string, cursorStr string, pageSize int) ([]*model.SystemNotification, string, bool, error) {
|
2026-03-21 03:21:07 +08:00
|
|
|
// 构建基础查询
|
2026-03-21 02:48:06 +08:00
|
|
|
query := r.db.Model(&model.SystemNotification{}).Where("receiver_id = ?", receiverID)
|
|
|
|
|
|
|
|
|
|
// 如果有游标,解析游标并添加条件
|
2026-03-21 03:21:07 +08:00
|
|
|
if cursorStr != "" {
|
|
|
|
|
decodedCursor, err := cursor.DecodeCursor(cursorStr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// 游标格式无效,返回空结果
|
|
|
|
|
return []*model.SystemNotification{}, "", false, nil
|
|
|
|
|
}
|
|
|
|
|
// 使用元组比较进行分页
|
|
|
|
|
condition := "(created_at, id) < (?, ?)"
|
|
|
|
|
query = query.Where(condition, decodedCursor.SortValue, decodedCursor.ID)
|
2026-03-21 02:48:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 多取一条用于判断是否还有下一页
|
|
|
|
|
var notifications []*model.SystemNotification
|
|
|
|
|
err := query.Order("created_at DESC, id DESC").
|
|
|
|
|
Limit(pageSize + 1).
|
|
|
|
|
Find(¬ifications).Error
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, "", false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hasMore := len(notifications) > pageSize
|
|
|
|
|
if hasMore {
|
|
|
|
|
notifications = notifications[:pageSize]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成下一页游标
|
|
|
|
|
var nextCursor string
|
|
|
|
|
if hasMore && len(notifications) > 0 {
|
|
|
|
|
lastNotification := notifications[len(notifications)-1]
|
2026-03-21 03:21:07 +08:00
|
|
|
nextCursor = cursor.NewCursor(
|
|
|
|
|
cursor.FormatTime(lastNotification.CreatedAt),
|
|
|
|
|
strconv.FormatInt(lastNotification.ID, 10),
|
|
|
|
|
cursor.SortByCreatedAtDesc,
|
|
|
|
|
).Encode()
|
2026-03-21 02:48:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return notifications, nextCursor, hasMore, nil
|
|
|
|
|
}
|