refactor: update repository interfaces and improve dependency injection
- Refactored repository structures to use interfaces instead of concrete types, enhancing flexibility and testability. - Updated various repository methods to accept interfaces, allowing for better dependency management. - Modified wire generation to accommodate new repository interfaces, ensuring proper service injection throughout the application. - Enhanced handler methods to utilize DTOs for filtering and data handling, improving code clarity and maintainability.
This commit is contained in:
@@ -55,7 +55,7 @@ func InitializeApp() (*App, error) {
|
||||
loginLogRepository := repository.NewLoginLogRepository(db)
|
||||
dataChangeLogRepository := repository.NewDataChangeLogRepository(db)
|
||||
logger := wire.ProvideLogger()
|
||||
asyncLogManager := wire.ProvideAsyncLogManager(db, operationLogRepository, loginLogRepository, dataChangeLogRepository, logger, config)
|
||||
asyncLogManager := wire.ProvideAsyncLogManager(operationLogRepository, loginLogRepository, dataChangeLogRepository, logger, config)
|
||||
operationLogService := wire.ProvideOperationLogService(asyncLogManager, operationLogRepository)
|
||||
loginLogService := wire.ProvideLoginLogService(asyncLogManager, loginLogRepository)
|
||||
dataChangeLogService := wire.ProvideDataChangeLogService(asyncLogManager, dataChangeLogRepository)
|
||||
@@ -70,10 +70,11 @@ func InitializeApp() (*App, error) {
|
||||
return nil, err
|
||||
}
|
||||
uploadService := wire.ProvideUploadService(s3Client, userService)
|
||||
chatService := wire.ProvideChatService(db, messageRepository, userRepository, hub, cache, uploadService)
|
||||
messageService := wire.ProvideMessageService(db, messageRepository, cache, uploadService)
|
||||
chatService := wire.ProvideChatService(messageRepository, userRepository, hub, cache, uploadService)
|
||||
messageService := wire.ProvideMessageService(messageRepository, cache, uploadService)
|
||||
groupRepository := repository.NewGroupRepository(db)
|
||||
groupService := wire.ProvideGroupService(db, groupRepository, userRepository, messageRepository, hub, cache)
|
||||
groupJoinRequestRepository := repository.NewGroupJoinRequestRepository(db)
|
||||
groupService := wire.ProvideGroupService(groupRepository, userRepository, messageRepository, groupJoinRequestRepository, systemNotificationRepository, hub, cache)
|
||||
messageHandler := wire.ProvideMessageHandler(chatService, messageService, userService, groupService, hub)
|
||||
notificationRepository := repository.NewNotificationRepository(db)
|
||||
notificationService := wire.ProvideNotificationService(notificationRepository, cache)
|
||||
@@ -81,7 +82,7 @@ func InitializeApp() (*App, error) {
|
||||
uploadHandler := handler.NewUploadHandler(uploadService)
|
||||
jwtService := wire.ProvideJWTService(config)
|
||||
pushHandler := handler.NewPushHandler(pushService)
|
||||
systemMessageHandler := wire.ProvideSystemMessageHandler(systemMessageService, systemNotificationRepository, cache)
|
||||
systemMessageHandler := wire.ProvideSystemMessageHandler(systemMessageService)
|
||||
groupHandler := wire.ProvideGroupHandler(groupService, userService)
|
||||
stickerRepository := repository.NewStickerRepository(db)
|
||||
stickerService := wire.ProvideStickerService(stickerRepository)
|
||||
|
||||
8
internal/cache/repository_adapter.go
vendored
8
internal/cache/repository_adapter.go
vendored
@@ -7,11 +7,11 @@ import (
|
||||
|
||||
// ConversationRepositoryAdapter 适配 MessageRepository 到 ConversationRepository 接口
|
||||
type ConversationRepositoryAdapter struct {
|
||||
repo *repository.MessageRepository
|
||||
repo repository.MessageRepository
|
||||
}
|
||||
|
||||
// NewConversationRepositoryAdapter 创建适配器
|
||||
func NewConversationRepositoryAdapter(repo *repository.MessageRepository) ConversationRepository {
|
||||
func NewConversationRepositoryAdapter(repo repository.MessageRepository) ConversationRepository {
|
||||
return &ConversationRepositoryAdapter{repo: repo}
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ func (a *ConversationRepositoryAdapter) GetUnreadCount(userID, convID string) (i
|
||||
|
||||
// MessageRepositoryAdapter 适配 MessageRepository 到 MessageRepository 接口
|
||||
type MessageRepositoryAdapter struct {
|
||||
repo *repository.MessageRepository
|
||||
repo repository.MessageRepository
|
||||
}
|
||||
|
||||
// NewMessageRepositoryAdapter 创建适配器
|
||||
func NewMessageRepositoryAdapter(repo *repository.MessageRepository) MessageRepository {
|
||||
func NewMessageRepositoryAdapter(repo repository.MessageRepository) MessageRepository {
|
||||
return &MessageRepositoryAdapter{repo: repo}
|
||||
}
|
||||
|
||||
|
||||
51
internal/dto/filter.go
Normal file
51
internal/dto/filter.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
// LogFilter 日志过滤条件
|
||||
type LogFilter struct {
|
||||
UserID string
|
||||
Operation string
|
||||
TargetType string
|
||||
TargetID string
|
||||
Status string
|
||||
IP string
|
||||
StartTime string
|
||||
EndTime string
|
||||
}
|
||||
|
||||
// LoginFilter 登录日志过滤条件
|
||||
type LoginFilter struct {
|
||||
UserID string
|
||||
Event string
|
||||
Result string
|
||||
FailReason string
|
||||
LoginType string
|
||||
IP string
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
}
|
||||
|
||||
// DataChangeFilter 数据变更日志过滤条件
|
||||
type DataChangeFilter struct {
|
||||
UserID string
|
||||
OperatorID string
|
||||
ChangeType string
|
||||
TargetType string
|
||||
OperatorType string
|
||||
IP string
|
||||
StartTime string
|
||||
EndTime string
|
||||
}
|
||||
|
||||
// MaterialFileQueryParams 学习资料查询参数
|
||||
type MaterialFileQueryParams struct {
|
||||
SubjectID string
|
||||
FileType string
|
||||
Status string
|
||||
Keyword string
|
||||
Page int
|
||||
PageSize int
|
||||
SortBy string
|
||||
SortOrder string
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/pkg/response"
|
||||
"carrot_bbs/internal/repository"
|
||||
"carrot_bbs/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -37,7 +37,7 @@ func (h *AdminLogHandler) GetOperationLogs(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
|
||||
filters := repository.LogFilter{
|
||||
filters := dto.LogFilter{
|
||||
UserID: c.Query("user_id"),
|
||||
Operation: c.Query("operation"),
|
||||
TargetType: c.Query("target_type"),
|
||||
@@ -70,7 +70,7 @@ func (h *AdminLogHandler) GetLoginLogs(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
|
||||
filters := repository.LoginFilter{
|
||||
filters := dto.LoginFilter{
|
||||
UserID: c.Query("user_id"),
|
||||
Event: c.Query("event"),
|
||||
Result: c.Query("result"),
|
||||
@@ -113,7 +113,7 @@ func (h *AdminLogHandler) GetDataChangeLogs(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
|
||||
filters := repository.DataChangeFilter{
|
||||
filters := dto.DataChangeFilter{
|
||||
UserID: c.Query("user_id"),
|
||||
OperatorID: c.Query("operator_id"),
|
||||
ChangeType: c.Query("change_type"),
|
||||
@@ -190,7 +190,7 @@ func (h *AdminLogHandler) ExportLogs(c *gin.Context) {
|
||||
"logs": logs,
|
||||
})
|
||||
case "login":
|
||||
logs, _, err := h.loginLogService.GetLoginLogs(c.Request.Context(), repository.LoginFilter{}, 1, 10000)
|
||||
logs, _, err := h.loginLogService.GetLoginLogs(c.Request.Context(), dto.LoginFilter{}, 1, 10000)
|
||||
if err != nil {
|
||||
response.HandleError(c, err, "failed to export login logs")
|
||||
return
|
||||
@@ -200,7 +200,7 @@ func (h *AdminLogHandler) ExportLogs(c *gin.Context) {
|
||||
"log": logs,
|
||||
})
|
||||
case "data_change":
|
||||
logs, _, err := h.dataChangeLogService.GetDataChangeLogs(c.Request.Context(), repository.DataChangeFilter{}, 1, 10000)
|
||||
logs, _, err := h.dataChangeLogService.GetDataChangeLogs(c.Request.Context(), dto.DataChangeFilter{}, 1, 10000)
|
||||
if err != nil {
|
||||
response.HandleError(c, err, "failed to export data change logs")
|
||||
return
|
||||
|
||||
@@ -3,9 +3,9 @@ package handler
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/model"
|
||||
"carrot_bbs/internal/pkg/response"
|
||||
"carrot_bbs/internal/repository"
|
||||
"carrot_bbs/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -147,7 +147,7 @@ func (h *MaterialHandler) ListMaterials(c *gin.Context) {
|
||||
fileType := c.Query("file_type")
|
||||
keyword := c.Query("keyword")
|
||||
|
||||
params := repository.MaterialFileQueryParams{
|
||||
params := dto.MaterialFileQueryParams{
|
||||
SubjectID: subjectID,
|
||||
FileType: fileType,
|
||||
Keyword: keyword,
|
||||
@@ -328,7 +328,7 @@ func (h *MaterialHandler) AdminListMaterials(c *gin.Context) {
|
||||
status := c.Query("status")
|
||||
keyword := c.Query("keyword")
|
||||
|
||||
params := repository.MaterialFileQueryParams{
|
||||
params := dto.MaterialFileQueryParams{
|
||||
SubjectID: subjectID,
|
||||
FileType: fileType,
|
||||
Status: status,
|
||||
|
||||
@@ -3,33 +3,24 @@ package handler
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"carrot_bbs/internal/cache"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/pkg/response"
|
||||
"carrot_bbs/internal/repository"
|
||||
"carrot_bbs/internal/service"
|
||||
)
|
||||
|
||||
// SystemMessageHandler 系统消息处理器
|
||||
type SystemMessageHandler struct {
|
||||
systemMsgService service.SystemMessageService
|
||||
notifyRepo *repository.SystemNotificationRepository
|
||||
cache cache.Cache
|
||||
}
|
||||
|
||||
// NewSystemMessageHandler 创建系统消息处理器
|
||||
func NewSystemMessageHandler(
|
||||
systemMsgService service.SystemMessageService,
|
||||
notifyRepo *repository.SystemNotificationRepository,
|
||||
cacheBackend cache.Cache,
|
||||
) *SystemMessageHandler {
|
||||
return &SystemMessageHandler{
|
||||
systemMsgService: systemMsgService,
|
||||
notifyRepo: notifyRepo,
|
||||
cache: cacheBackend,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +44,8 @@ func (h *SystemMessageHandler) GetSystemMessages(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
|
||||
// 获取当前用户的系统通知(从独立表中获取)
|
||||
notifications, total, err := h.notifyRepo.GetByReceiverID(userID, page, pageSize)
|
||||
// 获取当前用户的系统通知
|
||||
notifications, total, err := h.systemMsgService.GetNotifications(userID, page, pageSize)
|
||||
if err != nil {
|
||||
response.InternalServerError(c, "failed to get system messages")
|
||||
return
|
||||
@@ -83,7 +74,7 @@ func (h *SystemMessageHandler) GetSystemMessagesByCursor(c *gin.Context) {
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
|
||||
// 获取当前用户的系统通知(使用兼容旧版接口)
|
||||
notifications, nextCursor, hasMore, err := h.notifyRepo.GetByReceiverIDCursorLegacy(userID, cursorStr, pageSize)
|
||||
notifications, nextCursor, hasMore, err := h.systemMsgService.GetNotificationsByCursor(userID, cursorStr, pageSize)
|
||||
if err != nil {
|
||||
response.InternalServerError(c, "failed to get system messages")
|
||||
return
|
||||
@@ -117,7 +108,7 @@ func (h *SystemMessageHandler) GetUnreadCount(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 获取当前用户的未读通知数
|
||||
unreadCount, err := h.notifyRepo.GetUnreadCount(userID)
|
||||
unreadCount, err := h.systemMsgService.GetUnreadCount(userID)
|
||||
if err != nil {
|
||||
response.InternalServerError(c, "failed to get unread count")
|
||||
return
|
||||
@@ -145,12 +136,11 @@ func (h *SystemMessageHandler) MarkAsRead(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 标记为已读
|
||||
err = h.notifyRepo.MarkAsRead(notificationID, userID)
|
||||
err = h.systemMsgService.MarkAsRead(notificationID, userID)
|
||||
if err != nil {
|
||||
response.InternalServerError(c, "failed to mark as read")
|
||||
return
|
||||
}
|
||||
cache.InvalidateUnreadSystem(h.cache, userID)
|
||||
|
||||
response.SuccessWithMessage(c, "marked as read", nil)
|
||||
}
|
||||
@@ -165,12 +155,11 @@ func (h *SystemMessageHandler) MarkAllAsRead(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 标记当前用户所有通知为已读
|
||||
err := h.notifyRepo.MarkAllAsRead(userID)
|
||||
err := h.systemMsgService.MarkAllAsRead(userID)
|
||||
if err != nil {
|
||||
response.InternalServerError(c, "failed to mark all as read")
|
||||
return
|
||||
}
|
||||
cache.InvalidateUnreadSystem(h.cache, userID)
|
||||
|
||||
response.SuccessWithMessage(c, "all messages marked as read", nil)
|
||||
}
|
||||
@@ -192,12 +181,11 @@ func (h *SystemMessageHandler) DeleteSystemMessage(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 删除通知
|
||||
err = h.notifyRepo.Delete(notificationID, userID)
|
||||
err = h.systemMsgService.DeleteNotification(notificationID, userID)
|
||||
if err != nil {
|
||||
response.InternalServerError(c, "failed to delete notification")
|
||||
return
|
||||
}
|
||||
cache.InvalidateUnreadSystem(h.cache, userID)
|
||||
|
||||
response.SuccessWithMessage(c, "notification deleted", nil)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
type PostModerationHook struct {
|
||||
name string
|
||||
postAIService PostAIService
|
||||
postRepo *repository.PostRepository
|
||||
postRepo repository.PostRepository
|
||||
strictMode bool
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ type PostAIService interface {
|
||||
ModerateComment(ctx context.Context, content string, images []string) error
|
||||
}
|
||||
|
||||
func NewPostModerationHook(postAIService PostAIService, postRepo *repository.PostRepository, strictMode bool) *PostModerationHook {
|
||||
func NewPostModerationHook(postAIService PostAIService, postRepo repository.PostRepository, strictMode bool) *PostModerationHook {
|
||||
return &PostModerationHook{
|
||||
name: "moderation.post.ai",
|
||||
postAIService: postAIService,
|
||||
@@ -107,11 +107,11 @@ func (h *PostModerationHook) Execute(ctx *HookContext) error {
|
||||
type CommentModerationHook struct {
|
||||
name string
|
||||
postAIService PostAIService
|
||||
commentRepo *repository.CommentRepository
|
||||
commentRepo repository.CommentRepository
|
||||
strictMode bool
|
||||
}
|
||||
|
||||
func NewCommentModerationHook(postAIService PostAIService, commentRepo *repository.CommentRepository, strictMode bool) *CommentModerationHook {
|
||||
func NewCommentModerationHook(postAIService PostAIService, commentRepo repository.CommentRepository, strictMode bool) *CommentModerationHook {
|
||||
return &CommentModerationHook{
|
||||
name: "moderation.comment.ai",
|
||||
postAIService: postAIService,
|
||||
@@ -300,8 +300,8 @@ func (h *SensitiveWordHook) executeComment(ctx *HookContext) error {
|
||||
type ModerationHooks struct {
|
||||
postAIService PostAIService
|
||||
sensitiveService SensitiveService
|
||||
postRepo *repository.PostRepository
|
||||
commentRepo *repository.CommentRepository
|
||||
postRepo repository.PostRepository
|
||||
commentRepo repository.CommentRepository
|
||||
strictMode bool
|
||||
replaceStr string
|
||||
}
|
||||
@@ -309,8 +309,8 @@ type ModerationHooks struct {
|
||||
func NewModerationHooks(
|
||||
postAIService PostAIService,
|
||||
sensitiveService SensitiveService,
|
||||
postRepo *repository.PostRepository,
|
||||
commentRepo *repository.CommentRepository,
|
||||
postRepo repository.PostRepository,
|
||||
commentRepo repository.CommentRepository,
|
||||
strictMode bool,
|
||||
replaceStr string,
|
||||
) *ModerationHooks {
|
||||
|
||||
@@ -69,14 +69,46 @@ func (c *clientImpl) ModeratePost(ctx context.Context, title, content string, im
|
||||
if !c.IsEnabled() {
|
||||
return true, "", nil
|
||||
}
|
||||
return c.moderateContentInBatches(ctx, fmt.Sprintf("帖子标题:%s\n帖子内容:%s", title, content), images)
|
||||
|
||||
// 构建帖子提示词,处理标题或内容为空的情况
|
||||
var prompt strings.Builder
|
||||
if strings.TrimSpace(title) != "" {
|
||||
prompt.WriteString("帖子标题:" + title)
|
||||
}
|
||||
if strings.TrimSpace(content) != "" {
|
||||
if prompt.Len() > 0 {
|
||||
prompt.WriteString("\n")
|
||||
}
|
||||
prompt.WriteString("帖子内容:" + content)
|
||||
}
|
||||
// 如果标题和内容都为空但有图片,说明是纯图片帖子
|
||||
if prompt.Len() == 0 && len(normalizeImageURLs(images)) > 0 {
|
||||
prompt.WriteString("帖子内容:[仅包含图片]")
|
||||
}
|
||||
if prompt.Len() == 0 {
|
||||
prompt.WriteString("帖子内容:[空]")
|
||||
}
|
||||
|
||||
return c.moderateContentInBatches(ctx, prompt.String(), images)
|
||||
}
|
||||
|
||||
func (c *clientImpl) ModerateComment(ctx context.Context, content string, images []string) (bool, string, error) {
|
||||
if !c.IsEnabled() {
|
||||
return true, "", nil
|
||||
}
|
||||
return c.moderateContentInBatches(ctx, fmt.Sprintf("评论内容:%s", content), images)
|
||||
|
||||
// 构建评论提示词,处理纯图片评论的情况
|
||||
var prompt strings.Builder
|
||||
if strings.TrimSpace(content) != "" {
|
||||
prompt.WriteString("评论内容:" + content)
|
||||
} else if len(normalizeImageURLs(images)) > 0 {
|
||||
// 评论只有图片没有文字,说明是纯图片评论
|
||||
prompt.WriteString("评论内容:[仅包含图片]")
|
||||
} else {
|
||||
prompt.WriteString("评论内容:[空]")
|
||||
}
|
||||
|
||||
return c.moderateContentInBatches(ctx, prompt.String(), images)
|
||||
}
|
||||
|
||||
func (c *clientImpl) moderateContentInBatches(ctx context.Context, contentPrompt string, images []string) (bool, string, error) {
|
||||
|
||||
@@ -6,16 +6,26 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ChannelRepository 频道配置仓储
|
||||
type ChannelRepository struct {
|
||||
// ChannelRepository 频道配置仓储接口
|
||||
type ChannelRepository interface {
|
||||
ListActive() ([]*model.Channel, error)
|
||||
ListAll() ([]*model.Channel, error)
|
||||
Create(channel *model.Channel) error
|
||||
Update(channel *model.Channel) error
|
||||
GetByID(id string) (*model.Channel, error)
|
||||
Delete(id string) error
|
||||
}
|
||||
|
||||
// channelRepository 频道配置仓储实现
|
||||
type channelRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewChannelRepository(db *gorm.DB) *ChannelRepository {
|
||||
return &ChannelRepository{db: db}
|
||||
func NewChannelRepository(db *gorm.DB) ChannelRepository {
|
||||
return &channelRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *ChannelRepository) ListActive() ([]*model.Channel, error) {
|
||||
func (r *channelRepository) ListActive() ([]*model.Channel, error) {
|
||||
var channels []*model.Channel
|
||||
err := r.db.
|
||||
Where("is_active = ?", true).
|
||||
@@ -24,7 +34,7 @@ func (r *ChannelRepository) ListActive() ([]*model.Channel, error) {
|
||||
return channels, err
|
||||
}
|
||||
|
||||
func (r *ChannelRepository) ListAll() ([]*model.Channel, error) {
|
||||
func (r *channelRepository) ListAll() ([]*model.Channel, error) {
|
||||
var channels []*model.Channel
|
||||
err := r.db.
|
||||
Order("sort_order ASC, created_at ASC").
|
||||
@@ -32,15 +42,15 @@ func (r *ChannelRepository) ListAll() ([]*model.Channel, error) {
|
||||
return channels, err
|
||||
}
|
||||
|
||||
func (r *ChannelRepository) Create(channel *model.Channel) error {
|
||||
func (r *channelRepository) Create(channel *model.Channel) error {
|
||||
return r.db.Create(channel).Error
|
||||
}
|
||||
|
||||
func (r *ChannelRepository) Update(channel *model.Channel) error {
|
||||
func (r *channelRepository) Update(channel *model.Channel) error {
|
||||
return r.db.Save(channel).Error
|
||||
}
|
||||
|
||||
func (r *ChannelRepository) GetByID(id string) (*model.Channel, error) {
|
||||
func (r *channelRepository) GetByID(id string) (*model.Channel, error) {
|
||||
var channel model.Channel
|
||||
if err := r.db.First(&channel, "id = ?", id).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -48,7 +58,7 @@ func (r *ChannelRepository) GetByID(id string) (*model.Channel, error) {
|
||||
return &channel, nil
|
||||
}
|
||||
|
||||
func (r *ChannelRepository) Delete(id string) error {
|
||||
func (r *channelRepository) Delete(id string) error {
|
||||
return r.db.Delete(&model.Channel{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -8,23 +8,47 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CommentRepository 评论仓储
|
||||
type CommentRepository struct {
|
||||
// CommentRepository 评论仓储接口
|
||||
type CommentRepository interface {
|
||||
Create(comment *model.Comment) error
|
||||
GetByID(id string) (*model.Comment, error)
|
||||
Update(comment *model.Comment) error
|
||||
UpdateModerationStatus(commentID string, status model.CommentStatus) error
|
||||
Delete(id string) error
|
||||
ApplyPublishedStats(comment *model.Comment) error
|
||||
GetByPostID(postID string, page, pageSize int) ([]*model.Comment, int64, error)
|
||||
GetByPostIDWithReplies(postID string, page, pageSize, replyLimit int) ([]*model.Comment, int64, error)
|
||||
GetRepliesByRootID(rootID string, page, pageSize int) ([]*model.Comment, int64, error)
|
||||
GetReplies(parentID string) ([]*model.Comment, error)
|
||||
Like(commentID, userID string) error
|
||||
Unlike(commentID, userID string) error
|
||||
IsLiked(commentID, userID string) bool
|
||||
IsLikedBatch(commentIDs []string, userID string) map[string]bool
|
||||
GetAdminCommentList(filter AdminCommentListFilter) ([]*model.Comment, int64, error)
|
||||
GetAdminCommentByID(id string) (*model.Comment, error)
|
||||
BatchDelete(ids []string) ([]string, error)
|
||||
GetCommentsByPostIDForAdmin(postID string, page, pageSize int) ([]*model.Comment, int64, error)
|
||||
GetCommentsByCursor(ctx context.Context, postID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Comment], error)
|
||||
GetRepliesByCursor(ctx context.Context, rootID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Comment], error)
|
||||
}
|
||||
|
||||
// commentRepository 评论仓储实现
|
||||
type commentRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewCommentRepository 创建评论仓储
|
||||
func NewCommentRepository(db *gorm.DB) *CommentRepository {
|
||||
return &CommentRepository{db: db}
|
||||
func NewCommentRepository(db *gorm.DB) CommentRepository {
|
||||
return &commentRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建评论
|
||||
func (r *CommentRepository) Create(comment *model.Comment) error {
|
||||
func (r *commentRepository) Create(comment *model.Comment) error {
|
||||
return r.db.Create(comment).Error
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取评论
|
||||
func (r *CommentRepository) GetByID(id string) (*model.Comment, error) {
|
||||
func (r *commentRepository) GetByID(id string) (*model.Comment, error) {
|
||||
var comment model.Comment
|
||||
err := r.db.Preload("User").First(&comment, "id = ?", id).Error
|
||||
if err != nil {
|
||||
@@ -34,12 +58,12 @@ func (r *CommentRepository) GetByID(id string) (*model.Comment, error) {
|
||||
}
|
||||
|
||||
// Update 更新评论
|
||||
func (r *CommentRepository) Update(comment *model.Comment) error {
|
||||
func (r *commentRepository) Update(comment *model.Comment) error {
|
||||
return r.db.Save(comment).Error
|
||||
}
|
||||
|
||||
// UpdateModerationStatus 更新评论审核状态
|
||||
func (r *CommentRepository) UpdateModerationStatus(commentID string, status model.CommentStatus) error {
|
||||
func (r *commentRepository) UpdateModerationStatus(commentID string, status model.CommentStatus) error {
|
||||
// 审核状态更新属于管理操作,不应影响评论内容更新时间(updated_at)
|
||||
return r.db.Model(&model.Comment{}).
|
||||
Where("id = ?", commentID).
|
||||
@@ -47,7 +71,7 @@ func (r *CommentRepository) UpdateModerationStatus(commentID string, status mode
|
||||
}
|
||||
|
||||
// Delete 删除评论(软删除,同时清理关联数据)
|
||||
func (r *CommentRepository) Delete(id string) error {
|
||||
func (r *commentRepository) Delete(id string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 先查询评论获取post_id和parent_id
|
||||
var comment model.Comment
|
||||
@@ -91,7 +115,7 @@ func (r *CommentRepository) Delete(id string) error {
|
||||
}
|
||||
|
||||
// ApplyPublishedStats 在评论审核通过后更新帖子评论数/回复数
|
||||
func (r *CommentRepository) ApplyPublishedStats(comment *model.Comment) error {
|
||||
func (r *commentRepository) ApplyPublishedStats(comment *model.Comment) error {
|
||||
if comment == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -118,7 +142,7 @@ func (r *CommentRepository) ApplyPublishedStats(comment *model.Comment) error {
|
||||
}
|
||||
|
||||
// GetByPostID 获取帖子评论
|
||||
func (r *CommentRepository) GetByPostID(postID string, page, pageSize int) ([]*model.Comment, int64, error) {
|
||||
func (r *commentRepository) GetByPostID(postID string, page, pageSize int) ([]*model.Comment, int64, error) {
|
||||
var comments []*model.Comment
|
||||
var total int64
|
||||
|
||||
@@ -136,7 +160,7 @@ func (r *CommentRepository) GetByPostID(postID string, page, pageSize int) ([]*m
|
||||
|
||||
// GetByPostIDWithReplies 获取帖子评论(包含回复,扁平化结构)
|
||||
// 所有层级的回复都扁平化展示在顶级评论的 replies 中
|
||||
func (r *CommentRepository) GetByPostIDWithReplies(postID string, page, pageSize, replyLimit int) ([]*model.Comment, int64, error) {
|
||||
func (r *commentRepository) GetByPostIDWithReplies(postID string, page, pageSize, replyLimit int) ([]*model.Comment, int64, error) {
|
||||
var comments []*model.Comment
|
||||
var total int64
|
||||
|
||||
@@ -212,7 +236,7 @@ func (r *CommentRepository) GetByPostIDWithReplies(postID string, page, pageSize
|
||||
}
|
||||
|
||||
// loadFlatReplies 加载评论的所有回复(扁平化,所有层级都在同一层)
|
||||
func (r *CommentRepository) loadFlatReplies(rootComment *model.Comment, limit int) {
|
||||
func (r *commentRepository) loadFlatReplies(rootComment *model.Comment, limit int) {
|
||||
var allReplies []*model.Comment
|
||||
|
||||
// 查询所有以该评论为根评论的回复(不包括顶级评论本身)
|
||||
@@ -226,7 +250,7 @@ func (r *CommentRepository) loadFlatReplies(rootComment *model.Comment, limit in
|
||||
}
|
||||
|
||||
// GetRepliesByRootID 根据根评论ID分页获取回复(扁平化)
|
||||
func (r *CommentRepository) GetRepliesByRootID(rootID string, page, pageSize int) ([]*model.Comment, int64, error) {
|
||||
func (r *commentRepository) GetRepliesByRootID(rootID string, page, pageSize int) ([]*model.Comment, int64, error) {
|
||||
var replies []*model.Comment
|
||||
var total int64
|
||||
|
||||
@@ -246,7 +270,7 @@ func (r *CommentRepository) GetRepliesByRootID(rootID string, page, pageSize int
|
||||
}
|
||||
|
||||
// GetReplies 获取回复
|
||||
func (r *CommentRepository) GetReplies(parentID string) ([]*model.Comment, error) {
|
||||
func (r *commentRepository) GetReplies(parentID string) ([]*model.Comment, error) {
|
||||
var comments []*model.Comment
|
||||
err := r.db.Where("parent_id = ? AND status = ?", parentID, model.CommentStatusPublished).
|
||||
Preload("User").
|
||||
@@ -256,7 +280,7 @@ func (r *CommentRepository) GetReplies(parentID string) ([]*model.Comment, error
|
||||
}
|
||||
|
||||
// Like 点赞评论(使用事务保证数据一致性)
|
||||
func (r *CommentRepository) Like(commentID, userID string) error {
|
||||
func (r *commentRepository) Like(commentID, userID string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 检查是否已经点赞
|
||||
var existing model.CommentLike
|
||||
@@ -285,7 +309,7 @@ func (r *CommentRepository) Like(commentID, userID string) error {
|
||||
}
|
||||
|
||||
// Unlike 取消点赞评论(使用事务保证数据一致性)
|
||||
func (r *CommentRepository) Unlike(commentID, userID string) error {
|
||||
func (r *commentRepository) Unlike(commentID, userID string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
result := tx.Where("comment_id = ? AND user_id = ?", commentID, userID).Delete(&model.CommentLike{})
|
||||
if result.Error != nil {
|
||||
@@ -301,7 +325,7 @@ func (r *CommentRepository) Unlike(commentID, userID string) error {
|
||||
}
|
||||
|
||||
// IsLiked 检查是否已点赞
|
||||
func (r *CommentRepository) IsLiked(commentID, userID string) bool {
|
||||
func (r *commentRepository) IsLiked(commentID, userID string) bool {
|
||||
var count int64
|
||||
r.db.Model(&model.CommentLike{}).Where("comment_id = ? AND user_id = ?", commentID, userID).Count(&count)
|
||||
return count > 0
|
||||
@@ -309,7 +333,7 @@ func (r *CommentRepository) IsLiked(commentID, userID string) bool {
|
||||
|
||||
// IsLikedBatch 批量检查是否点赞(解决 N+1 问题)
|
||||
// 返回 map[commentID]bool
|
||||
func (r *CommentRepository) IsLikedBatch(commentIDs []string, userID string) map[string]bool {
|
||||
func (r *commentRepository) IsLikedBatch(commentIDs []string, userID string) map[string]bool {
|
||||
result := make(map[string]bool)
|
||||
if len(commentIDs) == 0 || userID == "" {
|
||||
return result
|
||||
@@ -347,7 +371,7 @@ type AdminCommentListFilter struct {
|
||||
}
|
||||
|
||||
// GetAdminCommentList 获取管理端评论列表
|
||||
func (r *CommentRepository) GetAdminCommentList(filter AdminCommentListFilter) ([]*model.Comment, int64, error) {
|
||||
func (r *commentRepository) GetAdminCommentList(filter AdminCommentListFilter) ([]*model.Comment, int64, error) {
|
||||
var comments []*model.Comment
|
||||
var total int64
|
||||
|
||||
@@ -388,7 +412,7 @@ func (r *CommentRepository) GetAdminCommentList(filter AdminCommentListFilter) (
|
||||
}
|
||||
|
||||
// GetAdminCommentByID 获取管理端评论详情(包含关联信息)
|
||||
func (r *CommentRepository) GetAdminCommentByID(id string) (*model.Comment, error) {
|
||||
func (r *commentRepository) GetAdminCommentByID(id string) (*model.Comment, error) {
|
||||
var comment model.Comment
|
||||
err := r.db.Preload("User").First(&comment, "id = ?", id).Error
|
||||
if err != nil {
|
||||
@@ -398,7 +422,7 @@ func (r *CommentRepository) GetAdminCommentByID(id string) (*model.Comment, erro
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除评论(使用单次事务批量删除,避免 N+1 问题)
|
||||
func (r *CommentRepository) BatchDelete(ids []string) ([]string, error) {
|
||||
func (r *commentRepository) BatchDelete(ids []string) ([]string, error) {
|
||||
if len(ids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -452,7 +476,7 @@ func (r *CommentRepository) BatchDelete(ids []string) ([]string, error) {
|
||||
}
|
||||
|
||||
// GetCommentsByPostIDForAdmin 管理端获取帖子的评论列表(包含所有状态)
|
||||
func (r *CommentRepository) GetCommentsByPostIDForAdmin(postID string, page, pageSize int) ([]*model.Comment, int64, error) {
|
||||
func (r *commentRepository) GetCommentsByPostIDForAdmin(postID string, page, pageSize int) ([]*model.Comment, int64, error) {
|
||||
var comments []*model.Comment
|
||||
var total int64
|
||||
|
||||
@@ -472,7 +496,7 @@ func (r *CommentRepository) GetCommentsByPostIDForAdmin(postID string, page, pag
|
||||
|
||||
// GetCommentsByCursor 游标分页获取帖子评论(包含回复)
|
||||
// 排序方式:created_at DESC
|
||||
func (r *CommentRepository) GetCommentsByCursor(ctx context.Context, postID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Comment], error) {
|
||||
func (r *commentRepository) GetCommentsByCursor(ctx context.Context, postID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Comment], error) {
|
||||
// 构建基础查询 - 只查询顶级评论
|
||||
query := r.db.WithContext(ctx).Model(&model.Comment{}).
|
||||
Where("post_id = ? AND parent_id IS NULL AND status = ?", postID, model.CommentStatusPublished)
|
||||
@@ -531,7 +555,7 @@ func (r *CommentRepository) GetCommentsByCursor(ctx context.Context, postID stri
|
||||
}
|
||||
|
||||
// loadRepliesForComments 为评论列表加载回复
|
||||
func (r *CommentRepository) loadRepliesForComments(comments []*model.Comment, replyLimit int) {
|
||||
func (r *commentRepository) loadRepliesForComments(comments []*model.Comment, replyLimit int) {
|
||||
if len(comments) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -593,7 +617,7 @@ func (r *CommentRepository) loadRepliesForComments(comments []*model.Comment, re
|
||||
|
||||
// GetRepliesByCursor 游标分页获取根评论的回复
|
||||
// 排序方式:created_at ASC(回复按时间正序)
|
||||
func (r *CommentRepository) GetRepliesByCursor(ctx context.Context, rootID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Comment], error) {
|
||||
func (r *commentRepository) GetRepliesByCursor(ctx context.Context, rootID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Comment], error) {
|
||||
// 构建基础查询
|
||||
query := r.db.WithContext(ctx).Model(&model.Comment{}).
|
||||
Where("root_id = ? AND status = ?", rootID, model.CommentStatusPublished)
|
||||
|
||||
@@ -3,28 +3,40 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// DataChangeLogRepository 数据变更日志仓储
|
||||
type DataChangeLogRepository struct {
|
||||
// DataChangeLogRepository 数据变更日志仓储接口
|
||||
type DataChangeLogRepository interface {
|
||||
CreateDataChangeLog(ctx context.Context, log *model.DataChangeLog) error
|
||||
BatchCreateDataChangeLogs(ctx context.Context, logs []*model.DataChangeLog) error
|
||||
GetDataChangeLogs(ctx context.Context, filters dto.DataChangeFilter, page, pageSize int) ([]*model.DataChangeLog, int64, error)
|
||||
GetDataChangeLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.DataChangeLog, int64, error)
|
||||
GetDataChangeLogsByOperator(ctx context.Context, operatorID string, page, pageSize int) ([]*model.DataChangeLog, int64, error)
|
||||
DeleteOldLogs(ctx context.Context, beforeDate string) error
|
||||
GetStatistics(ctx context.Context, startTime, endTime string) (*DataChangeLogStatistics, error)
|
||||
}
|
||||
|
||||
// dataChangeLogRepository 数据变更日志仓储实现
|
||||
type dataChangeLogRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewDataChangeLogRepository 创建数据变更日志仓储
|
||||
func NewDataChangeLogRepository(db *gorm.DB) *DataChangeLogRepository {
|
||||
return &DataChangeLogRepository{db: db}
|
||||
func NewDataChangeLogRepository(db *gorm.DB) DataChangeLogRepository {
|
||||
return &dataChangeLogRepository{db: db}
|
||||
}
|
||||
|
||||
// CreateDataChangeLog 创建单条数据变更日志
|
||||
func (r *DataChangeLogRepository) CreateDataChangeLog(ctx context.Context, log *model.DataChangeLog) error {
|
||||
func (r *dataChangeLogRepository) CreateDataChangeLog(ctx context.Context, log *model.DataChangeLog) error {
|
||||
return r.db.WithContext(ctx).Create(log).Error
|
||||
}
|
||||
|
||||
// BatchCreateDataChangeLogs 批量创建数据变更日志
|
||||
func (r *DataChangeLogRepository) BatchCreateDataChangeLogs(ctx context.Context, logs []*model.DataChangeLog) error {
|
||||
func (r *dataChangeLogRepository) BatchCreateDataChangeLogs(ctx context.Context, logs []*model.DataChangeLog) error {
|
||||
if len(logs) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -32,7 +44,7 @@ func (r *DataChangeLogRepository) BatchCreateDataChangeLogs(ctx context.Context,
|
||||
}
|
||||
|
||||
// GetDataChangeLogs 获取数据变更日志列表(分页)
|
||||
func (r *DataChangeLogRepository) GetDataChangeLogs(ctx context.Context, filters DataChangeFilter, page, pageSize int) ([]*model.DataChangeLog, int64, error) {
|
||||
func (r *dataChangeLogRepository) GetDataChangeLogs(ctx context.Context, filters dto.DataChangeFilter, page, pageSize int) ([]*model.DataChangeLog, int64, error) {
|
||||
query := r.db.WithContext(ctx).Model(&model.DataChangeLog{})
|
||||
|
||||
if filters.UserID != "" {
|
||||
@@ -75,22 +87,22 @@ func (r *DataChangeLogRepository) GetDataChangeLogs(ctx context.Context, filters
|
||||
}
|
||||
|
||||
// GetDataChangeLogsByUser 获取指定用户的数据变更日志
|
||||
func (r *DataChangeLogRepository) GetDataChangeLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.DataChangeLog, int64, error) {
|
||||
return r.GetDataChangeLogs(ctx, DataChangeFilter{UserID: userID}, page, pageSize)
|
||||
func (r *dataChangeLogRepository) GetDataChangeLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.DataChangeLog, int64, error) {
|
||||
return r.GetDataChangeLogs(ctx, dto.DataChangeFilter{UserID: userID}, page, pageSize)
|
||||
}
|
||||
|
||||
// GetDataChangeLogsByOperator 获取指定操作人的日志
|
||||
func (r *DataChangeLogRepository) GetDataChangeLogsByOperator(ctx context.Context, operatorID string, page, pageSize int) ([]*model.DataChangeLog, int64, error) {
|
||||
return r.GetDataChangeLogs(ctx, DataChangeFilter{OperatorID: operatorID}, page, pageSize)
|
||||
func (r *dataChangeLogRepository) GetDataChangeLogsByOperator(ctx context.Context, operatorID string, page, pageSize int) ([]*model.DataChangeLog, int64, error) {
|
||||
return r.GetDataChangeLogs(ctx, dto.DataChangeFilter{OperatorID: operatorID}, page, pageSize)
|
||||
}
|
||||
|
||||
// DeleteOldLogs 删除旧的数据变更日志(用于定时清理)
|
||||
func (r *DataChangeLogRepository) DeleteOldLogs(ctx context.Context, beforeDate string) error {
|
||||
func (r *dataChangeLogRepository) DeleteOldLogs(ctx context.Context, beforeDate string) error {
|
||||
return r.db.WithContext(ctx).Where("created_at < ?", beforeDate).Delete(&model.DataChangeLog{}).Error
|
||||
}
|
||||
|
||||
// GetStatistics 获取数据变更日志统计
|
||||
func (r *DataChangeLogRepository) GetStatistics(ctx context.Context, startTime, endTime string) (*DataChangeLogStatistics, error) {
|
||||
func (r *dataChangeLogRepository) GetStatistics(ctx context.Context, startTime, endTime string) (*DataChangeLogStatistics, error) {
|
||||
stats := &DataChangeLogStatistics{}
|
||||
|
||||
query := r.db.WithContext(ctx).Model(&model.DataChangeLog{})
|
||||
@@ -116,18 +128,6 @@ func (r *DataChangeLogRepository) GetStatistics(ctx context.Context, startTime,
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// DataChangeFilter 数据变更日志过滤条件
|
||||
type DataChangeFilter struct {
|
||||
UserID string
|
||||
OperatorID string
|
||||
ChangeType string
|
||||
TargetType string
|
||||
OperatorType string
|
||||
IP string
|
||||
StartTime string
|
||||
EndTime string
|
||||
}
|
||||
|
||||
// DataChangeLogStatistics 数据变更日志统计
|
||||
type DataChangeLogStatistics struct {
|
||||
TotalCount int64
|
||||
|
||||
@@ -8,23 +8,44 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// DeviceTokenRepository 设备Token仓储
|
||||
type DeviceTokenRepository struct {
|
||||
// DeviceTokenRepository 设备Token仓储接口
|
||||
type DeviceTokenRepository interface {
|
||||
Create(token *model.DeviceToken) error
|
||||
GetByID(id int64) (*model.DeviceToken, error)
|
||||
Update(token *model.DeviceToken) error
|
||||
Delete(id int64) error
|
||||
GetByUserID(userID string) ([]*model.DeviceToken, error)
|
||||
GetActiveByUserID(userID string) ([]*model.DeviceToken, error)
|
||||
GetByDeviceID(deviceID string) (*model.DeviceToken, error)
|
||||
GetByPushToken(pushToken string) (*model.DeviceToken, error)
|
||||
DeactivateAllExcept(userID int64, deviceID string) error
|
||||
Upsert(token *model.DeviceToken) error
|
||||
UpdateLastUsed(deviceID string) error
|
||||
Deactivate(deviceID string) error
|
||||
Activate(deviceID string) error
|
||||
DeleteByUserID(userID int64) error
|
||||
GetDeviceCountByUserID(userID int64) (int64, error)
|
||||
GetActiveDeviceCountByUserID(userID int64) (int64, error)
|
||||
DeleteInactiveDevices(before time.Time) error
|
||||
}
|
||||
|
||||
// deviceTokenRepository 设备Token仓储实现
|
||||
type deviceTokenRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewDeviceTokenRepository 创建设备Token仓储
|
||||
func NewDeviceTokenRepository(db *gorm.DB) *DeviceTokenRepository {
|
||||
return &DeviceTokenRepository{db: db}
|
||||
func NewDeviceTokenRepository(db *gorm.DB) DeviceTokenRepository {
|
||||
return &deviceTokenRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建设备Token
|
||||
func (r *DeviceTokenRepository) Create(token *model.DeviceToken) error {
|
||||
func (r *deviceTokenRepository) Create(token *model.DeviceToken) error {
|
||||
return r.db.Create(token).Error
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取设备Token
|
||||
func (r *DeviceTokenRepository) GetByID(id int64) (*model.DeviceToken, error) {
|
||||
func (r *deviceTokenRepository) GetByID(id int64) (*model.DeviceToken, error) {
|
||||
var token model.DeviceToken
|
||||
err := r.db.First(&token, "id = ?", id).Error
|
||||
if err != nil {
|
||||
@@ -34,18 +55,18 @@ func (r *DeviceTokenRepository) GetByID(id int64) (*model.DeviceToken, error) {
|
||||
}
|
||||
|
||||
// Update 更新设备Token
|
||||
func (r *DeviceTokenRepository) Update(token *model.DeviceToken) error {
|
||||
func (r *deviceTokenRepository) Update(token *model.DeviceToken) error {
|
||||
return r.db.Save(token).Error
|
||||
}
|
||||
|
||||
// Delete 删除设备Token(软删除)
|
||||
func (r *DeviceTokenRepository) Delete(id int64) error {
|
||||
func (r *deviceTokenRepository) Delete(id int64) error {
|
||||
return r.db.Delete(&model.DeviceToken{}, id).Error
|
||||
}
|
||||
|
||||
// GetByUserID 获取用户所有设备
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (r *DeviceTokenRepository) GetByUserID(userID string) ([]*model.DeviceToken, error) {
|
||||
func (r *deviceTokenRepository) GetByUserID(userID string) ([]*model.DeviceToken, error) {
|
||||
var tokens []*model.DeviceToken
|
||||
err := r.db.Where("user_id = ?", userID).
|
||||
Order("created_at DESC").
|
||||
@@ -55,7 +76,7 @@ func (r *DeviceTokenRepository) GetByUserID(userID string) ([]*model.DeviceToken
|
||||
|
||||
// GetActiveByUserID 获取用户活跃设备
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (r *DeviceTokenRepository) GetActiveByUserID(userID string) ([]*model.DeviceToken, error) {
|
||||
func (r *deviceTokenRepository) GetActiveByUserID(userID string) ([]*model.DeviceToken, error) {
|
||||
var tokens []*model.DeviceToken
|
||||
err := r.db.Where("user_id = ? AND is_active = ?", userID, true).
|
||||
Order("last_used_at DESC").
|
||||
@@ -64,7 +85,7 @@ func (r *DeviceTokenRepository) GetActiveByUserID(userID string) ([]*model.Devic
|
||||
}
|
||||
|
||||
// GetByDeviceID 根据设备ID获取设备Token
|
||||
func (r *DeviceTokenRepository) GetByDeviceID(deviceID string) (*model.DeviceToken, error) {
|
||||
func (r *deviceTokenRepository) GetByDeviceID(deviceID string) (*model.DeviceToken, error) {
|
||||
var token model.DeviceToken
|
||||
err := r.db.Where("device_id = ?", deviceID).First(&token).Error
|
||||
if err != nil {
|
||||
@@ -74,7 +95,7 @@ func (r *DeviceTokenRepository) GetByDeviceID(deviceID string) (*model.DeviceTok
|
||||
}
|
||||
|
||||
// GetByPushToken 根据推送Token获取设备信息
|
||||
func (r *DeviceTokenRepository) GetByPushToken(pushToken string) (*model.DeviceToken, error) {
|
||||
func (r *deviceTokenRepository) GetByPushToken(pushToken string) (*model.DeviceToken, error) {
|
||||
var token model.DeviceToken
|
||||
err := r.db.Where("push_token = ?", pushToken).First(&token).Error
|
||||
if err != nil {
|
||||
@@ -84,7 +105,7 @@ func (r *DeviceTokenRepository) GetByPushToken(pushToken string) (*model.DeviceT
|
||||
}
|
||||
|
||||
// DeactivateAllExcept 登出其他设备(停用除指定设备外的所有设备)
|
||||
func (r *DeviceTokenRepository) DeactivateAllExcept(userID int64, deviceID string) error {
|
||||
func (r *deviceTokenRepository) DeactivateAllExcept(userID int64, deviceID string) error {
|
||||
return r.db.Model(&model.DeviceToken{}).
|
||||
Where("user_id = ? AND device_id != ?", userID, deviceID).
|
||||
Update("is_active", false).Error
|
||||
@@ -92,7 +113,7 @@ func (r *DeviceTokenRepository) DeactivateAllExcept(userID int64, deviceID strin
|
||||
|
||||
// Upsert 创建或更新设备Token
|
||||
// 如果设备ID已存在,则更新Token和激活状态;否则创建新记录
|
||||
func (r *DeviceTokenRepository) Upsert(token *model.DeviceToken) error {
|
||||
func (r *deviceTokenRepository) Upsert(token *model.DeviceToken) error {
|
||||
var existing model.DeviceToken
|
||||
err := r.db.Where("device_id = ?", token.DeviceID).First(&existing).Error
|
||||
|
||||
@@ -113,21 +134,21 @@ func (r *DeviceTokenRepository) Upsert(token *model.DeviceToken) error {
|
||||
}
|
||||
|
||||
// UpdateLastUsed 更新最后使用时间
|
||||
func (r *DeviceTokenRepository) UpdateLastUsed(deviceID string) error {
|
||||
func (r *deviceTokenRepository) UpdateLastUsed(deviceID string) error {
|
||||
return r.db.Model(&model.DeviceToken{}).
|
||||
Where("device_id = ?", deviceID).
|
||||
Update("last_used_at", time.Now()).Error
|
||||
}
|
||||
|
||||
// Deactivate 停用设备
|
||||
func (r *DeviceTokenRepository) Deactivate(deviceID string) error {
|
||||
func (r *deviceTokenRepository) Deactivate(deviceID string) error {
|
||||
return r.db.Model(&model.DeviceToken{}).
|
||||
Where("device_id = ?", deviceID).
|
||||
Update("is_active", false).Error
|
||||
}
|
||||
|
||||
// Activate 激活设备
|
||||
func (r *DeviceTokenRepository) Activate(deviceID string) error {
|
||||
func (r *deviceTokenRepository) Activate(deviceID string) error {
|
||||
return r.db.Model(&model.DeviceToken{}).
|
||||
Where("device_id = ?", deviceID).
|
||||
Updates(map[string]interface{}{
|
||||
@@ -137,12 +158,12 @@ func (r *DeviceTokenRepository) Activate(deviceID string) error {
|
||||
}
|
||||
|
||||
// DeleteByUserID 删除用户所有设备Token
|
||||
func (r *DeviceTokenRepository) DeleteByUserID(userID int64) error {
|
||||
func (r *deviceTokenRepository) DeleteByUserID(userID int64) error {
|
||||
return r.db.Where("user_id = ?", userID).Delete(&model.DeviceToken{}).Error
|
||||
}
|
||||
|
||||
// GetDeviceCountByUserID 获取用户设备数量
|
||||
func (r *DeviceTokenRepository) GetDeviceCountByUserID(userID int64) (int64, error) {
|
||||
func (r *deviceTokenRepository) GetDeviceCountByUserID(userID int64) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.DeviceToken{}).
|
||||
Where("user_id = ?", userID).
|
||||
@@ -151,7 +172,7 @@ func (r *DeviceTokenRepository) GetDeviceCountByUserID(userID int64) (int64, err
|
||||
}
|
||||
|
||||
// GetActiveDeviceCountByUserID 获取用户活跃设备数量
|
||||
func (r *DeviceTokenRepository) GetActiveDeviceCountByUserID(userID int64) (int64, error) {
|
||||
func (r *deviceTokenRepository) GetActiveDeviceCountByUserID(userID int64) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.DeviceToken{}).
|
||||
Where("user_id = ? AND is_active = ?", userID, true).
|
||||
@@ -160,7 +181,7 @@ func (r *DeviceTokenRepository) GetActiveDeviceCountByUserID(userID int64) (int6
|
||||
}
|
||||
|
||||
// DeleteInactiveDevices 删除长时间未使用的设备
|
||||
func (r *DeviceTokenRepository) DeleteInactiveDevices(before time.Time) error {
|
||||
func (r *deviceTokenRepository) DeleteInactiveDevices(before time.Time) error {
|
||||
return r.db.Where("is_active = ? AND last_used_at < ?", false, before).
|
||||
Delete(&model.DeviceToken{}).Error
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ type GroupRepository interface {
|
||||
Update(group *model.Group) error
|
||||
Delete(id string) error
|
||||
GetByOwnerID(ownerID string, page, pageSize int) ([]model.Group, int64, error)
|
||||
TransferOwner(groupID, oldOwnerID, newOwnerID string) error
|
||||
|
||||
// 群成员操作
|
||||
AddMember(member *model.GroupMember) error
|
||||
@@ -90,6 +91,32 @@ func (r *groupRepository) Update(group *model.Group) error {
|
||||
return r.db.Save(group).Error
|
||||
}
|
||||
|
||||
// TransferOwner 转让群主(在事务中更新群主和角色)
|
||||
func (r *groupRepository) TransferOwner(groupID, oldOwnerID, newOwnerID string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 更新群组的群主
|
||||
if err := tx.Model(&model.Group{}).Where("id = ?", groupID).Update("owner_id", newOwnerID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新原群主为管理员
|
||||
if err := tx.Model(&model.GroupMember{}).
|
||||
Where("group_id = ? AND user_id = ?", groupID, oldOwnerID).
|
||||
Update("role", model.GroupRoleAdmin).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新新群主为群主
|
||||
if err := tx.Model(&model.GroupMember{}).
|
||||
Where("group_id = ? AND user_id = ?", groupID, newOwnerID).
|
||||
Update("role", model.GroupRoleOwner).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除群组
|
||||
func (r *groupRepository) Delete(id string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
|
||||
@@ -4,28 +4,41 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// LoginLogRepository 登录日志仓储
|
||||
type LoginLogRepository struct {
|
||||
// LoginLogRepository 登录日志仓储接口
|
||||
type LoginLogRepository interface {
|
||||
CreateLoginLog(ctx context.Context, log *model.LoginLog) error
|
||||
BatchCreateLoginLogs(ctx context.Context, logs []*model.LoginLog) error
|
||||
GetLoginLogs(ctx context.Context, filters dto.LoginFilter, page, pageSize int) ([]*model.LoginLog, int64, error)
|
||||
GetLoginLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.LoginLog, int64, error)
|
||||
GetFailedLoginsByIP(ctx context.Context, ip string, timeWindow time.Duration) (int64, error)
|
||||
GetRecentSuccessLogins(ctx context.Context, userID string, limit int) ([]*model.LoginLog, error)
|
||||
DeleteOldLogs(ctx context.Context, beforeDate string) error
|
||||
GetStatistics(ctx context.Context, startTime, endTime string) (*LoginLogStatistics, error)
|
||||
}
|
||||
|
||||
// loginLogRepository 登录日志仓储实现
|
||||
type loginLogRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewLoginLogRepository 创建登录日志仓储
|
||||
func NewLoginLogRepository(db *gorm.DB) *LoginLogRepository {
|
||||
return &LoginLogRepository{db: db}
|
||||
func NewLoginLogRepository(db *gorm.DB) LoginLogRepository {
|
||||
return &loginLogRepository{db: db}
|
||||
}
|
||||
|
||||
// CreateLoginLog 创建单条登录日志
|
||||
func (r *LoginLogRepository) CreateLoginLog(ctx context.Context, log *model.LoginLog) error {
|
||||
func (r *loginLogRepository) CreateLoginLog(ctx context.Context, log *model.LoginLog) error {
|
||||
return r.db.WithContext(ctx).Create(log).Error
|
||||
}
|
||||
|
||||
// BatchCreateLoginLogs 批量创建登录日志
|
||||
func (r *LoginLogRepository) BatchCreateLoginLogs(ctx context.Context, logs []*model.LoginLog) error {
|
||||
func (r *loginLogRepository) BatchCreateLoginLogs(ctx context.Context, logs []*model.LoginLog) error {
|
||||
if len(logs) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -33,7 +46,7 @@ func (r *LoginLogRepository) BatchCreateLoginLogs(ctx context.Context, logs []*m
|
||||
}
|
||||
|
||||
// GetLoginLogs 获取登录日志列表(分页)
|
||||
func (r *LoginLogRepository) GetLoginLogs(ctx context.Context, filters LoginFilter, page, pageSize int) ([]*model.LoginLog, int64, error) {
|
||||
func (r *loginLogRepository) GetLoginLogs(ctx context.Context, filters dto.LoginFilter, page, pageSize int) ([]*model.LoginLog, int64, error) {
|
||||
query := r.db.WithContext(ctx).Model(&model.LoginLog{})
|
||||
|
||||
if filters.UserID != "" {
|
||||
@@ -76,12 +89,12 @@ func (r *LoginLogRepository) GetLoginLogs(ctx context.Context, filters LoginFilt
|
||||
}
|
||||
|
||||
// GetLoginLogsByUser 获取指定用户的登录日志
|
||||
func (r *LoginLogRepository) GetLoginLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.LoginLog, int64, error) {
|
||||
return r.GetLoginLogs(ctx, LoginFilter{UserID: userID}, page, pageSize)
|
||||
func (r *loginLogRepository) GetLoginLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.LoginLog, int64, error) {
|
||||
return r.GetLoginLogs(ctx, dto.LoginFilter{UserID: userID}, page, pageSize)
|
||||
}
|
||||
|
||||
// GetFailedLoginsByIP 获取指定IP的失败登录记录(用于风控)
|
||||
func (r *LoginLogRepository) GetFailedLoginsByIP(ctx context.Context, ip string, timeWindow time.Duration) (int64, error) {
|
||||
func (r *loginLogRepository) GetFailedLoginsByIP(ctx context.Context, ip string, timeWindow time.Duration) (int64, error) {
|
||||
startTime := time.Now().Add(-timeWindow)
|
||||
var count int64
|
||||
err := r.db.WithContext(ctx).
|
||||
@@ -92,7 +105,7 @@ func (r *LoginLogRepository) GetFailedLoginsByIP(ctx context.Context, ip string,
|
||||
}
|
||||
|
||||
// GetRecentSuccessLogins 获取最近成功的登录记录
|
||||
func (r *LoginLogRepository) GetRecentSuccessLogins(ctx context.Context, userID string, limit int) ([]*model.LoginLog, error) {
|
||||
func (r *loginLogRepository) GetRecentSuccessLogins(ctx context.Context, userID string, limit int) ([]*model.LoginLog, error) {
|
||||
var logs []*model.LoginLog
|
||||
err := r.db.WithContext(ctx).
|
||||
Where("user_id = ? AND result = ? AND event = ?", userID, string(model.LoginResultSuccess), string(model.LoginEventLogin)).
|
||||
@@ -103,12 +116,12 @@ func (r *LoginLogRepository) GetRecentSuccessLogins(ctx context.Context, userID
|
||||
}
|
||||
|
||||
// DeleteOldLogs 删除旧的登录日志(用于定时清理)
|
||||
func (r *LoginLogRepository) DeleteOldLogs(ctx context.Context, beforeDate string) error {
|
||||
func (r *loginLogRepository) DeleteOldLogs(ctx context.Context, beforeDate string) error {
|
||||
return r.db.WithContext(ctx).Where("created_at < ?", beforeDate).Delete(&model.LoginLog{}).Error
|
||||
}
|
||||
|
||||
// GetStatistics 获取登录日志统计(使用单次 GROUP BY 查询,避免多次 COUNT)
|
||||
func (r *LoginLogRepository) GetStatistics(ctx context.Context, startTime, endTime string) (*LoginLogStatistics, error) {
|
||||
func (r *loginLogRepository) GetStatistics(ctx context.Context, startTime, endTime string) (*LoginLogStatistics, error) {
|
||||
stats := &LoginLogStatistics{}
|
||||
|
||||
query := r.db.WithContext(ctx).Model(&model.LoginLog{})
|
||||
@@ -155,18 +168,6 @@ func (r *LoginLogRepository) GetStatistics(ctx context.Context, startTime, endTi
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// LoginFilter 登录日志过滤条件
|
||||
type LoginFilter struct {
|
||||
UserID string
|
||||
Event string
|
||||
Result string
|
||||
FailReason string
|
||||
LoginType string
|
||||
IP string
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
}
|
||||
|
||||
// LoginLogStatistics 登录日志统计
|
||||
type LoginLogStatistics struct {
|
||||
TotalCount int64
|
||||
|
||||
@@ -1,22 +1,34 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// MaterialSubjectRepository 学科分类仓储
|
||||
type MaterialSubjectRepository struct {
|
||||
// MaterialSubjectRepository 学科分类仓储接口
|
||||
type MaterialSubjectRepository interface {
|
||||
ListActive() ([]*model.MaterialSubject, error)
|
||||
ListAll() ([]*model.MaterialSubject, error)
|
||||
GetByID(id string) (*model.MaterialSubject, error)
|
||||
Create(subject *model.MaterialSubject) error
|
||||
Update(subject *model.MaterialSubject) error
|
||||
Delete(id string) error
|
||||
CountMaterials(subjectID string) (int64, error)
|
||||
}
|
||||
|
||||
// materialSubjectRepository 学科分类仓储实现
|
||||
type materialSubjectRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewMaterialSubjectRepository(db *gorm.DB) *MaterialSubjectRepository {
|
||||
return &MaterialSubjectRepository{db: db}
|
||||
func NewMaterialSubjectRepository(db *gorm.DB) MaterialSubjectRepository {
|
||||
return &materialSubjectRepository{db: db}
|
||||
}
|
||||
|
||||
// ListActive 获取所有启用的学科分类
|
||||
func (r *MaterialSubjectRepository) ListActive() ([]*model.MaterialSubject, error) {
|
||||
func (r *materialSubjectRepository) ListActive() ([]*model.MaterialSubject, error) {
|
||||
var subjects []*model.MaterialSubject
|
||||
err := r.db.
|
||||
Where("is_active = ?", true).
|
||||
@@ -26,7 +38,7 @@ func (r *MaterialSubjectRepository) ListActive() ([]*model.MaterialSubject, erro
|
||||
}
|
||||
|
||||
// ListAll 获取所有学科分类(管理端用)
|
||||
func (r *MaterialSubjectRepository) ListAll() ([]*model.MaterialSubject, error) {
|
||||
func (r *materialSubjectRepository) ListAll() ([]*model.MaterialSubject, error) {
|
||||
var subjects []*model.MaterialSubject
|
||||
err := r.db.
|
||||
Order("sort_order ASC, created_at ASC").
|
||||
@@ -35,7 +47,7 @@ func (r *MaterialSubjectRepository) ListAll() ([]*model.MaterialSubject, error)
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取学科分类
|
||||
func (r *MaterialSubjectRepository) GetByID(id string) (*model.MaterialSubject, error) {
|
||||
func (r *materialSubjectRepository) GetByID(id string) (*model.MaterialSubject, error) {
|
||||
var subject model.MaterialSubject
|
||||
if err := r.db.First(&subject, "id = ?", id).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -44,50 +56,53 @@ func (r *MaterialSubjectRepository) GetByID(id string) (*model.MaterialSubject,
|
||||
}
|
||||
|
||||
// Create 创建学科分类
|
||||
func (r *MaterialSubjectRepository) Create(subject *model.MaterialSubject) error {
|
||||
func (r *materialSubjectRepository) Create(subject *model.MaterialSubject) error {
|
||||
return r.db.Create(subject).Error
|
||||
}
|
||||
|
||||
// Update 更新学科分类
|
||||
func (r *MaterialSubjectRepository) Update(subject *model.MaterialSubject) error {
|
||||
func (r *materialSubjectRepository) Update(subject *model.MaterialSubject) error {
|
||||
return r.db.Save(subject).Error
|
||||
}
|
||||
|
||||
// Delete 删除学科分类
|
||||
func (r *MaterialSubjectRepository) Delete(id string) error {
|
||||
func (r *materialSubjectRepository) Delete(id string) error {
|
||||
return r.db.Delete(&model.MaterialSubject{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
// CountMaterials 统计学科下的资料数量
|
||||
func (r *MaterialSubjectRepository) CountMaterials(subjectID string) (int64, error) {
|
||||
func (r *materialSubjectRepository) CountMaterials(subjectID string) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.MaterialFile{}).Where("subject_id = ?", subjectID).Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
// MaterialFileRepository 文件资料仓储
|
||||
type MaterialFileRepository struct {
|
||||
// MaterialFileRepository 文件资料仓储接口
|
||||
type MaterialFileRepository interface {
|
||||
List(params dto.MaterialFileQueryParams) ([]*model.MaterialFile, int64, error)
|
||||
GetByID(id string) (*model.MaterialFile, error)
|
||||
GetByIDWithSubject(id string) (*model.MaterialFile, error)
|
||||
Create(file *model.MaterialFile) error
|
||||
Update(file *model.MaterialFile) error
|
||||
Delete(id string) error
|
||||
IncrementDownloadCount(id string) error
|
||||
GetBySubjectID(subjectID string, page, pageSize int) ([]*model.MaterialFile, int64, error)
|
||||
Search(keyword string, page, pageSize int) ([]*model.MaterialFile, int64, error)
|
||||
GetHotMaterials(limit int) ([]*model.MaterialFile, error)
|
||||
GetLatestMaterials(limit int) ([]*model.MaterialFile, error)
|
||||
}
|
||||
|
||||
// materialFileRepository 文件资料仓储实现
|
||||
type materialFileRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewMaterialFileRepository(db *gorm.DB) *MaterialFileRepository {
|
||||
return &MaterialFileRepository{db: db}
|
||||
}
|
||||
|
||||
// ListQueryParams 查询参数
|
||||
type MaterialFileQueryParams struct {
|
||||
SubjectID string
|
||||
FileType string
|
||||
Status string
|
||||
Keyword string
|
||||
Page int
|
||||
PageSize int
|
||||
SortBy string
|
||||
SortOrder string
|
||||
func NewMaterialFileRepository(db *gorm.DB) MaterialFileRepository {
|
||||
return &materialFileRepository{db: db}
|
||||
}
|
||||
|
||||
// List 获取资料列表(支持筛选和分页)
|
||||
func (r *MaterialFileRepository) List(params MaterialFileQueryParams) ([]*model.MaterialFile, int64, error) {
|
||||
func (r *materialFileRepository) List(params dto.MaterialFileQueryParams) ([]*model.MaterialFile, int64, error) {
|
||||
var files []*model.MaterialFile
|
||||
var total int64
|
||||
|
||||
@@ -144,7 +159,7 @@ func (r *MaterialFileRepository) List(params MaterialFileQueryParams) ([]*model.
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取资料详情
|
||||
func (r *MaterialFileRepository) GetByID(id string) (*model.MaterialFile, error) {
|
||||
func (r *materialFileRepository) GetByID(id string) (*model.MaterialFile, error) {
|
||||
var file model.MaterialFile
|
||||
if err := r.db.First(&file, "id = ?", id).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -153,7 +168,7 @@ func (r *MaterialFileRepository) GetByID(id string) (*model.MaterialFile, error)
|
||||
}
|
||||
|
||||
// GetByIDWithSubject 根据ID获取资料详情(包含学科信息)
|
||||
func (r *MaterialFileRepository) GetByIDWithSubject(id string) (*model.MaterialFile, error) {
|
||||
func (r *materialFileRepository) GetByIDWithSubject(id string) (*model.MaterialFile, error) {
|
||||
var file model.MaterialFile
|
||||
if err := r.db.Preload("Subject").First(&file, "id = ?", id).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -162,28 +177,28 @@ func (r *MaterialFileRepository) GetByIDWithSubject(id string) (*model.MaterialF
|
||||
}
|
||||
|
||||
// Create 创建资料
|
||||
func (r *MaterialFileRepository) Create(file *model.MaterialFile) error {
|
||||
func (r *materialFileRepository) Create(file *model.MaterialFile) error {
|
||||
return r.db.Create(file).Error
|
||||
}
|
||||
|
||||
// Update 更新资料
|
||||
func (r *MaterialFileRepository) Update(file *model.MaterialFile) error {
|
||||
func (r *materialFileRepository) Update(file *model.MaterialFile) error {
|
||||
return r.db.Save(file).Error
|
||||
}
|
||||
|
||||
// Delete 删除资料
|
||||
func (r *MaterialFileRepository) Delete(id string) error {
|
||||
func (r *materialFileRepository) Delete(id string) error {
|
||||
return r.db.Delete(&model.MaterialFile{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
// IncrementDownloadCount 增加下载次数
|
||||
func (r *MaterialFileRepository) IncrementDownloadCount(id string) error {
|
||||
func (r *materialFileRepository) IncrementDownloadCount(id string) error {
|
||||
return r.db.Model(&model.MaterialFile{}).Where("id = ?", id).
|
||||
UpdateColumn("download_count", gorm.Expr("download_count + ?", 1)).Error
|
||||
}
|
||||
|
||||
// GetBySubjectID 根据学科ID获取资料列表
|
||||
func (r *MaterialFileRepository) GetBySubjectID(subjectID string, page, pageSize int) ([]*model.MaterialFile, int64, error) {
|
||||
func (r *materialFileRepository) GetBySubjectID(subjectID string, page, pageSize int) ([]*model.MaterialFile, int64, error) {
|
||||
var files []*model.MaterialFile
|
||||
var total int64
|
||||
|
||||
@@ -202,7 +217,7 @@ func (r *MaterialFileRepository) GetBySubjectID(subjectID string, page, pageSize
|
||||
}
|
||||
|
||||
// Search 搜索资料
|
||||
func (r *MaterialFileRepository) Search(keyword string, page, pageSize int) ([]*model.MaterialFile, int64, error) {
|
||||
func (r *materialFileRepository) Search(keyword string, page, pageSize int) ([]*model.MaterialFile, int64, error) {
|
||||
var files []*model.MaterialFile
|
||||
var total int64
|
||||
|
||||
@@ -225,7 +240,7 @@ func (r *MaterialFileRepository) Search(keyword string, page, pageSize int) ([]*
|
||||
}
|
||||
|
||||
// GetHotMaterials 获取热门资料(按下载次数排序)
|
||||
func (r *MaterialFileRepository) GetHotMaterials(limit int) ([]*model.MaterialFile, error) {
|
||||
func (r *materialFileRepository) GetHotMaterials(limit int) ([]*model.MaterialFile, error) {
|
||||
var files []*model.MaterialFile
|
||||
err := r.db.
|
||||
Where("status = ?", model.MaterialStatusActive).
|
||||
@@ -236,7 +251,7 @@ func (r *MaterialFileRepository) GetHotMaterials(limit int) ([]*model.MaterialFi
|
||||
}
|
||||
|
||||
// GetLatestMaterials 获取最新资料
|
||||
func (r *MaterialFileRepository) GetLatestMaterials(limit int) ([]*model.MaterialFile, error) {
|
||||
func (r *materialFileRepository) GetLatestMaterials(limit int) ([]*model.MaterialFile, error) {
|
||||
var files []*model.MaterialFile
|
||||
err := r.db.
|
||||
Where("status = ?", model.MaterialStatusActive).
|
||||
|
||||
@@ -14,23 +14,64 @@ import (
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
// MessageRepository 消息仓储
|
||||
type MessageRepository struct {
|
||||
// MessageRepository 消息仓储接口
|
||||
type MessageRepository interface {
|
||||
CreateMessage(msg *model.Message) error
|
||||
GetConversation(id string) (*model.Conversation, error)
|
||||
GetOrCreatePrivateConversation(user1ID, user2ID string) (*model.Conversation, error)
|
||||
CreateConversationWithParticipants(conv *model.Conversation, participants []string) error
|
||||
GetConversations(userID string, page, pageSize int) ([]*model.Conversation, int64, error)
|
||||
GetMessages(conversationID string, page, pageSize int) ([]*model.Message, int64, error)
|
||||
GetMessagesAfterSeq(conversationID string, afterSeq int64, limit int) ([]*model.Message, error)
|
||||
GetMessagesBeforeSeq(conversationID string, beforeSeq int64, limit int) ([]*model.Message, error)
|
||||
GetConversationParticipants(conversationID string) ([]*model.ConversationParticipant, error)
|
||||
GetParticipant(conversationID string, userID string) (*model.ConversationParticipant, error)
|
||||
UpdateLastReadSeq(conversationID string, userID string, lastReadSeq int64) error
|
||||
UpdatePinned(conversationID string, userID string, isPinned bool) error
|
||||
GetUnreadCount(conversationID string, userID string) (int64, error)
|
||||
UpdateConversationLastSeq(conversationID string, seq int64) error
|
||||
GetNextSeq(conversationID string) (int64, error)
|
||||
CreateMessageWithSeq(msg *model.Message) error
|
||||
GetAllUnreadCount(userID string) (int64, error)
|
||||
GetMessageByID(messageID string) (*model.Message, error)
|
||||
CountMessagesBySenderInConversation(conversationID, senderID string) (int64, error)
|
||||
UpdateMessageStatus(messageID string, status model.MessageStatus) error
|
||||
RecallMessage(messageID string, userID string) error
|
||||
GetOrCreateSystemParticipant(userID string) (*model.ConversationParticipant, error)
|
||||
GetSystemMessagesUnreadCount(userID string) (int64, error)
|
||||
MarkAllSystemMessagesAsRead(userID string) error
|
||||
GetConversationByGroupID(groupID string) (*model.Conversation, error)
|
||||
RemoveParticipant(conversationID string, userID string) error
|
||||
AddParticipant(conversationID string, userID string) error
|
||||
DeleteConversationByGroupID(groupID string) error
|
||||
HideConversationForUser(conversationID, userID string) error
|
||||
BatchWriteMessages(ctx context.Context, messages []*model.Message) error
|
||||
BatchUpdateParticipants(ctx context.Context, updates []ParticipantUpdate) error
|
||||
UpdateConversationLastSeqWithContext(ctx context.Context, convID string, lastSeq int64, lastMsgTime time.Time) error
|
||||
BatchWriteMessagesWithTx(tx *gorm.DB, messages []*model.Message) error
|
||||
BatchUpdateParticipantsWithTx(tx *gorm.DB, updates []ParticipantUpdate) error
|
||||
UpdateConversationLastSeqWithTx(tx *gorm.DB, convID string, lastSeq int64, lastMsgTime time.Time) error
|
||||
GetMessagesByCursor(ctx context.Context, conversationID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Message], error)
|
||||
GetConversationsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Conversation], error)
|
||||
}
|
||||
|
||||
// messageRepository 消息仓储实现
|
||||
type messageRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewMessageRepository 创建消息仓储
|
||||
func NewMessageRepository(db *gorm.DB) *MessageRepository {
|
||||
return &MessageRepository{db: db}
|
||||
func NewMessageRepository(db *gorm.DB) MessageRepository {
|
||||
return &messageRepository{db: db}
|
||||
}
|
||||
|
||||
// CreateMessage 创建消息
|
||||
func (r *MessageRepository) CreateMessage(msg *model.Message) error {
|
||||
func (r *messageRepository) CreateMessage(msg *model.Message) error {
|
||||
return r.db.Create(msg).Error
|
||||
}
|
||||
|
||||
// GetConversation 获取会话
|
||||
func (r *MessageRepository) GetConversation(id string) (*model.Conversation, error) {
|
||||
func (r *messageRepository) GetConversation(id string) (*model.Conversation, error) {
|
||||
var conv model.Conversation
|
||||
err := r.db.Preload("Group").First(&conv, "id = ?", id).Error
|
||||
if err != nil {
|
||||
@@ -39,10 +80,32 @@ func (r *MessageRepository) GetConversation(id string) (*model.Conversation, err
|
||||
return &conv, nil
|
||||
}
|
||||
|
||||
// CreateConversationWithParticipants 创建会话并添加参与者(在事务中)
|
||||
func (r *messageRepository) CreateConversationWithParticipants(conv *model.Conversation, participants []string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Create(conv).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, userID := range participants {
|
||||
participant := model.ConversationParticipant{
|
||||
ConversationID: conv.ID,
|
||||
UserID: userID,
|
||||
LastReadSeq: 0,
|
||||
}
|
||||
if err := tx.Create(&participant).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetOrCreatePrivateConversation 获取或创建私聊会话
|
||||
// 使用参与者关系表来管理会话
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (r *MessageRepository) GetOrCreatePrivateConversation(user1ID, user2ID string) (*model.Conversation, error) {
|
||||
func (r *messageRepository) GetOrCreatePrivateConversation(user1ID, user2ID string) (*model.Conversation, error) {
|
||||
var conv model.Conversation
|
||||
|
||||
// 查找两个用户共同参与的私聊会话
|
||||
@@ -91,7 +154,7 @@ func (r *MessageRepository) GetOrCreatePrivateConversation(user1ID, user2ID stri
|
||||
|
||||
// GetConversations 获取用户会话列表
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (r *MessageRepository) GetConversations(userID string, page, pageSize int) ([]*model.Conversation, int64, error) {
|
||||
func (r *messageRepository) GetConversations(userID string, page, pageSize int) ([]*model.Conversation, int64, error) {
|
||||
var convs []*model.Conversation
|
||||
var total int64
|
||||
|
||||
@@ -121,7 +184,7 @@ func (r *MessageRepository) GetConversations(userID string, page, pageSize int)
|
||||
}
|
||||
|
||||
// GetMessages 获取会话消息
|
||||
func (r *MessageRepository) GetMessages(conversationID string, page, pageSize int) ([]*model.Message, int64, error) {
|
||||
func (r *messageRepository) GetMessages(conversationID string, page, pageSize int) ([]*model.Message, int64, error) {
|
||||
var messages []*model.Message
|
||||
var total int64
|
||||
|
||||
@@ -142,7 +205,7 @@ func (r *MessageRepository) GetMessages(conversationID string, page, pageSize in
|
||||
}
|
||||
|
||||
// GetMessagesAfterSeq 获取指定seq之后的消息(用于增量同步)
|
||||
func (r *MessageRepository) GetMessagesAfterSeq(conversationID string, afterSeq int64, limit int) ([]*model.Message, error) {
|
||||
func (r *messageRepository) GetMessagesAfterSeq(conversationID string, afterSeq int64, limit int) ([]*model.Message, error) {
|
||||
var messages []*model.Message
|
||||
err := r.db.Where("conversation_id = ? AND seq > ?", conversationID, afterSeq).
|
||||
Order("seq ASC").
|
||||
@@ -157,7 +220,7 @@ func (r *MessageRepository) GetMessagesAfterSeq(conversationID string, afterSeq
|
||||
}
|
||||
|
||||
// GetMessagesBeforeSeq 获取指定seq之前的历史消息(用于下拉加载更多)
|
||||
func (r *MessageRepository) GetMessagesBeforeSeq(conversationID string, beforeSeq int64, limit int) ([]*model.Message, error) {
|
||||
func (r *messageRepository) GetMessagesBeforeSeq(conversationID string, beforeSeq int64, limit int) ([]*model.Message, error) {
|
||||
var messages []*model.Message
|
||||
err := r.db.Where("conversation_id = ? AND seq < ?", conversationID, beforeSeq).
|
||||
Order("seq DESC"). // 降序获取最新消息在前
|
||||
@@ -176,7 +239,7 @@ func (r *MessageRepository) GetMessagesBeforeSeq(conversationID string, beforeSe
|
||||
}
|
||||
|
||||
// GetConversationParticipants 获取会话参与者
|
||||
func (r *MessageRepository) GetConversationParticipants(conversationID string) ([]*model.ConversationParticipant, error) {
|
||||
func (r *messageRepository) GetConversationParticipants(conversationID string) ([]*model.ConversationParticipant, error) {
|
||||
var participants []*model.ConversationParticipant
|
||||
err := r.db.Where("conversation_id = ?", conversationID).Find(&participants).Error
|
||||
return participants, err
|
||||
@@ -184,7 +247,7 @@ func (r *MessageRepository) GetConversationParticipants(conversationID string) (
|
||||
|
||||
// GetParticipant 获取用户在会话中的参与者信息
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (r *MessageRepository) GetParticipant(conversationID string, userID string) (*model.ConversationParticipant, error) {
|
||||
func (r *messageRepository) GetParticipant(conversationID string, userID string) (*model.ConversationParticipant, error) {
|
||||
var participant model.ConversationParticipant
|
||||
err := r.db.Where("conversation_id = ? AND user_id = ?", conversationID, userID).First(&participant).Error
|
||||
if err != nil {
|
||||
@@ -211,7 +274,7 @@ func (r *MessageRepository) GetParticipant(conversationID string, userID string)
|
||||
|
||||
// UpdateLastReadSeq 更新已读位置
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (r *MessageRepository) UpdateLastReadSeq(conversationID string, userID string, lastReadSeq int64) error {
|
||||
func (r *messageRepository) UpdateLastReadSeq(conversationID string, userID string, lastReadSeq int64) error {
|
||||
result := r.db.Model(&model.ConversationParticipant{}).
|
||||
Where("conversation_id = ? AND user_id = ?", conversationID, userID).
|
||||
Update("last_read_seq", lastReadSeq)
|
||||
@@ -246,7 +309,7 @@ func (r *MessageRepository) UpdateLastReadSeq(conversationID string, userID stri
|
||||
}
|
||||
|
||||
// UpdatePinned 更新会话置顶状态(用户维度)
|
||||
func (r *MessageRepository) UpdatePinned(conversationID string, userID string, isPinned bool) error {
|
||||
func (r *messageRepository) UpdatePinned(conversationID string, userID string, isPinned bool) error {
|
||||
result := r.db.Model(&model.ConversationParticipant{}).
|
||||
Where("conversation_id = ? AND user_id = ?", conversationID, userID).
|
||||
Update("is_pinned", isPinned)
|
||||
@@ -277,7 +340,7 @@ func (r *MessageRepository) UpdatePinned(conversationID string, userID string, i
|
||||
|
||||
// GetUnreadCount 获取未读消息数
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (r *MessageRepository) GetUnreadCount(conversationID string, userID string) (int64, error) {
|
||||
func (r *messageRepository) GetUnreadCount(conversationID string, userID string) (int64, error) {
|
||||
var participant model.ConversationParticipant
|
||||
err := r.db.Where("conversation_id = ? AND user_id = ?", conversationID, userID).First(&participant).Error
|
||||
if err != nil {
|
||||
@@ -292,7 +355,7 @@ func (r *MessageRepository) GetUnreadCount(conversationID string, userID string)
|
||||
}
|
||||
|
||||
// UpdateConversationLastSeq 更新会话的最后消息seq和时间
|
||||
func (r *MessageRepository) UpdateConversationLastSeq(conversationID string, seq int64) error {
|
||||
func (r *messageRepository) UpdateConversationLastSeq(conversationID string, seq int64) error {
|
||||
return r.db.Model(&model.Conversation{}).
|
||||
Where("id = ?", conversationID).
|
||||
Updates(map[string]interface{}{
|
||||
@@ -302,7 +365,7 @@ func (r *MessageRepository) UpdateConversationLastSeq(conversationID string, seq
|
||||
}
|
||||
|
||||
// GetNextSeq 获取会话的下一个seq值
|
||||
func (r *MessageRepository) GetNextSeq(conversationID string) (int64, error) {
|
||||
func (r *messageRepository) GetNextSeq(conversationID string) (int64, error) {
|
||||
var conv model.Conversation
|
||||
err := r.db.Select("last_seq").Where("id = ?", conversationID).First(&conv).Error
|
||||
if err != nil {
|
||||
@@ -312,7 +375,7 @@ func (r *MessageRepository) GetNextSeq(conversationID string) (int64, error) {
|
||||
}
|
||||
|
||||
// CreateMessageWithSeq 创建消息并更新seq(事务操作)
|
||||
func (r *MessageRepository) CreateMessageWithSeq(msg *model.Message) error {
|
||||
func (r *messageRepository) CreateMessageWithSeq(msg *model.Message) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 获取当前seq并+1
|
||||
var conv model.Conversation
|
||||
@@ -350,7 +413,7 @@ func (r *MessageRepository) CreateMessageWithSeq(msg *model.Message) error {
|
||||
|
||||
// GetAllUnreadCount 获取用户所有会话的未读消息总数
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (r *MessageRepository) GetAllUnreadCount(userID string) (int64, error) {
|
||||
func (r *messageRepository) GetAllUnreadCount(userID string) (int64, error) {
|
||||
var totalUnread int64
|
||||
err := r.db.Table("conversation_participants AS cp").
|
||||
Joins("LEFT JOIN messages AS m ON m.conversation_id = cp.conversation_id AND m.sender_id <> ? AND m.seq > cp.last_read_seq AND m.deleted_at IS NULL", userID).
|
||||
@@ -361,7 +424,7 @@ func (r *MessageRepository) GetAllUnreadCount(userID string) (int64, error) {
|
||||
}
|
||||
|
||||
// GetMessageByID 根据ID获取消息
|
||||
func (r *MessageRepository) GetMessageByID(messageID string) (*model.Message, error) {
|
||||
func (r *messageRepository) GetMessageByID(messageID string) (*model.Message, error) {
|
||||
var message model.Message
|
||||
err := r.db.First(&message, "id = ?", messageID).Error
|
||||
if err != nil {
|
||||
@@ -373,7 +436,7 @@ func (r *MessageRepository) GetMessageByID(messageID string) (*model.Message, er
|
||||
}
|
||||
|
||||
// CountMessagesBySenderInConversation 统计会话中某用户已发送消息数
|
||||
func (r *MessageRepository) CountMessagesBySenderInConversation(conversationID, senderID string) (int64, error) {
|
||||
func (r *messageRepository) CountMessagesBySenderInConversation(conversationID, senderID string) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.Message{}).
|
||||
Where("conversation_id = ? AND sender_id = ?", conversationID, senderID).
|
||||
@@ -382,15 +445,25 @@ func (r *MessageRepository) CountMessagesBySenderInConversation(conversationID,
|
||||
}
|
||||
|
||||
// UpdateMessageStatus 更新消息状态
|
||||
func (r *MessageRepository) UpdateMessageStatus(messageID int64, status model.MessageStatus) error {
|
||||
func (r *messageRepository) UpdateMessageStatus(messageID string, status model.MessageStatus) error {
|
||||
return r.db.Model(&model.Message{}).
|
||||
Where("id = ?", messageID).
|
||||
Update("status", status).Error
|
||||
}
|
||||
|
||||
// RecallMessage 撤回消息(2分钟内)
|
||||
func (r *messageRepository) RecallMessage(messageID string, userID string) error {
|
||||
return r.db.Model(&model.Message{}).
|
||||
Where("id = ? AND sender_id = ?", messageID, userID).
|
||||
Updates(map[string]interface{}{
|
||||
"status": model.MessageStatusRecalled,
|
||||
"segments": model.MessageSegments{},
|
||||
}).Error
|
||||
}
|
||||
|
||||
// GetOrCreateSystemParticipant 获取或创建用户在系统会话中的参与者记录
|
||||
// 系统会话是虚拟会话,但需要参与者记录来跟踪已读状态
|
||||
func (r *MessageRepository) GetOrCreateSystemParticipant(userID string) (*model.ConversationParticipant, error) {
|
||||
func (r *messageRepository) GetOrCreateSystemParticipant(userID string) (*model.ConversationParticipant, error) {
|
||||
var participant model.ConversationParticipant
|
||||
err := r.db.Where("conversation_id = ? AND user_id = ?",
|
||||
model.SystemConversationID, userID).First(&participant).Error
|
||||
@@ -418,7 +491,7 @@ func (r *MessageRepository) GetOrCreateSystemParticipant(userID string) (*model.
|
||||
}
|
||||
|
||||
// GetSystemMessagesUnreadCount 获取系统消息未读数
|
||||
func (r *MessageRepository) GetSystemMessagesUnreadCount(userID string) (int64, error) {
|
||||
func (r *messageRepository) GetSystemMessagesUnreadCount(userID string) (int64, error) {
|
||||
// 获取或创建参与者记录
|
||||
participant, err := r.GetOrCreateSystemParticipant(userID)
|
||||
if err != nil {
|
||||
@@ -436,7 +509,7 @@ func (r *MessageRepository) GetSystemMessagesUnreadCount(userID string) (int64,
|
||||
}
|
||||
|
||||
// MarkAllSystemMessagesAsRead 标记所有系统消息已读
|
||||
func (r *MessageRepository) MarkAllSystemMessagesAsRead(userID string) error {
|
||||
func (r *messageRepository) MarkAllSystemMessagesAsRead(userID string) error {
|
||||
// 获取系统会话的最新 seq
|
||||
var maxSeq int64
|
||||
err := r.db.Model(&model.Message{}).
|
||||
@@ -465,7 +538,7 @@ func (r *MessageRepository) MarkAllSystemMessagesAsRead(userID string) error {
|
||||
}
|
||||
|
||||
// GetConversationByGroupID 通过群组ID获取会话
|
||||
func (r *MessageRepository) GetConversationByGroupID(groupID string) (*model.Conversation, error) {
|
||||
func (r *messageRepository) GetConversationByGroupID(groupID string) (*model.Conversation, error) {
|
||||
var conv model.Conversation
|
||||
err := r.db.Where("group_id = ?", groupID).First(&conv).Error
|
||||
if err != nil {
|
||||
@@ -476,14 +549,14 @@ func (r *MessageRepository) GetConversationByGroupID(groupID string) (*model.Con
|
||||
|
||||
// RemoveParticipant 移除会话参与者
|
||||
// 当用户退出群聊时,需要同时移除其在对应会话中的参与者记录
|
||||
func (r *MessageRepository) RemoveParticipant(conversationID string, userID string) error {
|
||||
func (r *messageRepository) RemoveParticipant(conversationID string, userID string) error {
|
||||
return r.db.Where("conversation_id = ? AND user_id = ?", conversationID, userID).
|
||||
Delete(&model.ConversationParticipant{}).Error
|
||||
}
|
||||
|
||||
// AddParticipant 添加会话参与者
|
||||
// 当用户加入群聊时,需要同时将其添加到对应会话的参与者记录
|
||||
func (r *MessageRepository) AddParticipant(conversationID string, userID string) error {
|
||||
func (r *messageRepository) AddParticipant(conversationID string, userID string) error {
|
||||
// 先检查是否已经是参与者
|
||||
var count int64
|
||||
err := r.db.Model(&model.ConversationParticipant{}).
|
||||
@@ -509,7 +582,7 @@ func (r *MessageRepository) AddParticipant(conversationID string, userID string)
|
||||
|
||||
// DeleteConversationByGroupID 删除群组对应的会话及其参与者
|
||||
// 当解散群组时调用
|
||||
func (r *MessageRepository) DeleteConversationByGroupID(groupID string) error {
|
||||
func (r *messageRepository) DeleteConversationByGroupID(groupID string) error {
|
||||
// 获取群组对应的会话
|
||||
conv, err := r.GetConversationByGroupID(groupID)
|
||||
if err != nil {
|
||||
@@ -538,7 +611,7 @@ func (r *MessageRepository) DeleteConversationByGroupID(groupID string) error {
|
||||
}
|
||||
|
||||
// HideConversationForUser 仅对当前用户隐藏会话(私聊删除)
|
||||
func (r *MessageRepository) HideConversationForUser(conversationID, userID string) error {
|
||||
func (r *messageRepository) HideConversationForUser(conversationID, userID string) error {
|
||||
now := time.Now()
|
||||
return r.db.Model(&model.ConversationParticipant{}).
|
||||
Where("conversation_id = ? AND user_id = ?", conversationID, userID).
|
||||
@@ -554,7 +627,7 @@ type ParticipantUpdate struct {
|
||||
|
||||
// BatchWriteMessages 批量写入消息
|
||||
// 使用 GORM 的 CreateInBatches 实现高效批量插入
|
||||
func (r *MessageRepository) BatchWriteMessages(ctx context.Context, messages []*model.Message) error {
|
||||
func (r *messageRepository) BatchWriteMessages(ctx context.Context, messages []*model.Message) error {
|
||||
if len(messages) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -563,7 +636,7 @@ func (r *MessageRepository) BatchWriteMessages(ctx context.Context, messages []*
|
||||
|
||||
// BatchUpdateParticipants 批量更新参与者(使用 CASE WHEN 优化)
|
||||
// 使用单条 SQL 更新多条记录,避免循环执行 UPDATE
|
||||
func (r *MessageRepository) BatchUpdateParticipants(ctx context.Context, updates []ParticipantUpdate) error {
|
||||
func (r *messageRepository) BatchUpdateParticipants(ctx context.Context, updates []ParticipantUpdate) error {
|
||||
if len(updates) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -601,7 +674,7 @@ func (r *MessageRepository) BatchUpdateParticipants(ctx context.Context, updates
|
||||
}
|
||||
|
||||
// UpdateConversationLastSeqWithContext 更新会话最后消息序号
|
||||
func (r *MessageRepository) UpdateConversationLastSeqWithContext(ctx context.Context, convID string, lastSeq int64, lastMsgTime time.Time) error {
|
||||
func (r *messageRepository) UpdateConversationLastSeqWithContext(ctx context.Context, convID string, lastSeq int64, lastMsgTime time.Time) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Model(&model.Conversation{}).
|
||||
Where("id = ?", convID).
|
||||
@@ -613,7 +686,7 @@ func (r *MessageRepository) UpdateConversationLastSeqWithContext(ctx context.Con
|
||||
}
|
||||
|
||||
// BatchWriteMessagesWithTx 在事务中批量写入消息
|
||||
func (r *MessageRepository) BatchWriteMessagesWithTx(tx *gorm.DB, messages []*model.Message) error {
|
||||
func (r *messageRepository) BatchWriteMessagesWithTx(tx *gorm.DB, messages []*model.Message) error {
|
||||
if len(messages) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -621,7 +694,7 @@ func (r *MessageRepository) BatchWriteMessagesWithTx(tx *gorm.DB, messages []*mo
|
||||
}
|
||||
|
||||
// BatchUpdateParticipantsWithTx 在事务中批量更新参与者
|
||||
func (r *MessageRepository) BatchUpdateParticipantsWithTx(tx *gorm.DB, updates []ParticipantUpdate) error {
|
||||
func (r *messageRepository) BatchUpdateParticipantsWithTx(tx *gorm.DB, updates []ParticipantUpdate) error {
|
||||
if len(updates) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -649,7 +722,7 @@ func (r *MessageRepository) BatchUpdateParticipantsWithTx(tx *gorm.DB, updates [
|
||||
}
|
||||
|
||||
// UpdateConversationLastSeqWithTx 在事务中更新会话最后消息序号
|
||||
func (r *MessageRepository) UpdateConversationLastSeqWithTx(tx *gorm.DB, convID string, lastSeq int64, lastMsgTime time.Time) error {
|
||||
func (r *messageRepository) UpdateConversationLastSeqWithTx(tx *gorm.DB, convID string, lastSeq int64, lastMsgTime time.Time) error {
|
||||
return tx.Model(&model.Conversation{}).
|
||||
Where("id = ?", convID).
|
||||
Updates(map[string]interface{}{
|
||||
@@ -663,7 +736,7 @@ func (r *MessageRepository) UpdateConversationLastSeqWithTx(tx *gorm.DB, convID
|
||||
|
||||
// GetMessagesByCursor 游标分页获取会话消息
|
||||
// 消息按 seq DESC 排序
|
||||
func (r *MessageRepository) GetMessagesByCursor(ctx context.Context, conversationID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Message], error) {
|
||||
func (r *messageRepository) GetMessagesByCursor(ctx context.Context, conversationID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Message], error) {
|
||||
// 构建基础查询
|
||||
query := r.db.WithContext(ctx).Model(&model.Message{}).Where("conversation_id = ?", conversationID)
|
||||
|
||||
@@ -722,7 +795,7 @@ func (r *MessageRepository) GetMessagesByCursor(ctx context.Context, conversatio
|
||||
// GetConversationsByCursor 游标分页获取用户会话列表
|
||||
// 会话按置顶优先,然后按 updated_at DESC 排序
|
||||
// 注意:会话列表的排序涉及 conversation_participants 表的 is_pinned 和 updated_at 字段
|
||||
func (r *MessageRepository) GetConversationsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Conversation], error) {
|
||||
func (r *messageRepository) GetConversationsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Conversation], error) {
|
||||
// 构建基础查询 - 关联 conversation_participants 表
|
||||
query := r.db.WithContext(ctx).Model(&model.Conversation{}).
|
||||
Joins("INNER JOIN conversation_participants cp ON conversations.id = cp.conversation_id").
|
||||
|
||||
@@ -8,23 +8,36 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// NotificationRepository 通知仓储
|
||||
type NotificationRepository struct {
|
||||
// NotificationRepository 通知仓储接口
|
||||
type NotificationRepository interface {
|
||||
Create(notification *model.Notification) error
|
||||
GetByID(id string) (*model.Notification, error)
|
||||
GetByUserID(userID string, page, pageSize int, unreadOnly bool) ([]*model.Notification, int64, error)
|
||||
MarkAsRead(id string) error
|
||||
MarkAllAsRead(userID string) error
|
||||
Delete(id string) error
|
||||
GetUnreadCount(userID string) (int64, error)
|
||||
DeleteAllByUserID(userID string) error
|
||||
GetNotificationsByCursor(ctx context.Context, userID string, unreadOnly bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Notification], error)
|
||||
}
|
||||
|
||||
// notificationRepository 通知仓储实现
|
||||
type notificationRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewNotificationRepository 创建通知仓储
|
||||
func NewNotificationRepository(db *gorm.DB) *NotificationRepository {
|
||||
return &NotificationRepository{db: db}
|
||||
func NewNotificationRepository(db *gorm.DB) NotificationRepository {
|
||||
return ¬ificationRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建通知
|
||||
func (r *NotificationRepository) Create(notification *model.Notification) error {
|
||||
func (r *notificationRepository) Create(notification *model.Notification) error {
|
||||
return r.db.Create(notification).Error
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取通知
|
||||
func (r *NotificationRepository) GetByID(id string) (*model.Notification, error) {
|
||||
func (r *notificationRepository) GetByID(id string) (*model.Notification, error) {
|
||||
var notification model.Notification
|
||||
err := r.db.First(¬ification, "id = ?", id).Error
|
||||
if err != nil {
|
||||
@@ -34,7 +47,7 @@ func (r *NotificationRepository) GetByID(id string) (*model.Notification, error)
|
||||
}
|
||||
|
||||
// GetByUserID 获取用户通知
|
||||
func (r *NotificationRepository) GetByUserID(userID string, page, pageSize int, unreadOnly bool) ([]*model.Notification, int64, error) {
|
||||
func (r *notificationRepository) GetByUserID(userID string, page, pageSize int, unreadOnly bool) ([]*model.Notification, int64, error) {
|
||||
var notifications []*model.Notification
|
||||
var total int64
|
||||
|
||||
@@ -53,29 +66,29 @@ func (r *NotificationRepository) GetByUserID(userID string, page, pageSize int,
|
||||
}
|
||||
|
||||
// MarkAsRead 标记为已读
|
||||
func (r *NotificationRepository) MarkAsRead(id string) error {
|
||||
func (r *notificationRepository) MarkAsRead(id string) error {
|
||||
return r.db.Model(&model.Notification{}).Where("id = ?", id).Update("is_read", true).Error
|
||||
}
|
||||
|
||||
// MarkAllAsRead 标记所有为已读
|
||||
func (r *NotificationRepository) MarkAllAsRead(userID string) error {
|
||||
func (r *notificationRepository) MarkAllAsRead(userID string) error {
|
||||
return r.db.Model(&model.Notification{}).Where("user_id = ?", userID).Update("is_read", true).Error
|
||||
}
|
||||
|
||||
// Delete 删除通知
|
||||
func (r *NotificationRepository) Delete(id string) error {
|
||||
func (r *notificationRepository) Delete(id string) error {
|
||||
return r.db.Delete(&model.Notification{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
// GetUnreadCount 获取未读数量
|
||||
func (r *NotificationRepository) GetUnreadCount(userID string) (int64, error) {
|
||||
func (r *notificationRepository) GetUnreadCount(userID string) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.Notification{}).Where("user_id = ? AND is_read = ?", userID, false).Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
// DeleteAllByUserID 删除用户所有通知
|
||||
func (r *NotificationRepository) DeleteAllByUserID(userID string) error {
|
||||
func (r *notificationRepository) DeleteAllByUserID(userID string) error {
|
||||
return r.db.Where("user_id = ?", userID).Delete(&model.Notification{}).Error
|
||||
}
|
||||
|
||||
@@ -83,7 +96,7 @@ func (r *NotificationRepository) DeleteAllByUserID(userID string) error {
|
||||
|
||||
// GetNotificationsByCursor 游标分页获取用户通知
|
||||
// 排序方式:created_at DESC
|
||||
func (r *NotificationRepository) GetNotificationsByCursor(ctx context.Context, userID string, unreadOnly bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Notification], error) {
|
||||
func (r *notificationRepository) GetNotificationsByCursor(ctx context.Context, userID string, unreadOnly bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Notification], error) {
|
||||
// 构建基础查询
|
||||
query := r.db.WithContext(ctx).Model(&model.Notification{}).Where("user_id = ?", userID)
|
||||
|
||||
|
||||
@@ -3,28 +3,40 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// OperationLogRepository 操作日志仓储
|
||||
type OperationLogRepository struct {
|
||||
// OperationLogRepository 操作日志仓储接口
|
||||
type OperationLogRepository interface {
|
||||
CreateOperationLog(ctx context.Context, log *model.OperationLog) error
|
||||
BatchCreateOperationLogs(ctx context.Context, logs []*model.OperationLog) error
|
||||
GetOperationLogs(ctx context.Context, filters dto.LogFilter, page, pageSize int) ([]*model.OperationLog, int64, error)
|
||||
GetOperationLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.OperationLog, int64, error)
|
||||
GetOperationLogsByTimeRange(ctx context.Context, startTime, endTime string, page, pageSize int) ([]*model.OperationLog, int64, error)
|
||||
DeleteOldLogs(ctx context.Context, beforeDate string) error
|
||||
GetStatistics(ctx context.Context, startTime, endTime string) (*OperationLogStatistics, error)
|
||||
}
|
||||
|
||||
// operationLogRepository 操作日志仓储实现
|
||||
type operationLogRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewOperationLogRepository 创建操作日志仓储
|
||||
func NewOperationLogRepository(db *gorm.DB) *OperationLogRepository {
|
||||
return &OperationLogRepository{db: db}
|
||||
func NewOperationLogRepository(db *gorm.DB) OperationLogRepository {
|
||||
return &operationLogRepository{db: db}
|
||||
}
|
||||
|
||||
// CreateOperationLog 创建单条操作日志
|
||||
func (r *OperationLogRepository) CreateOperationLog(ctx context.Context, log *model.OperationLog) error {
|
||||
func (r *operationLogRepository) CreateOperationLog(ctx context.Context, log *model.OperationLog) error {
|
||||
return r.db.WithContext(ctx).Create(log).Error
|
||||
}
|
||||
|
||||
// BatchCreateOperationLogs 批量创建操作日志
|
||||
func (r *OperationLogRepository) BatchCreateOperationLogs(ctx context.Context, logs []*model.OperationLog) error {
|
||||
func (r *operationLogRepository) BatchCreateOperationLogs(ctx context.Context, logs []*model.OperationLog) error {
|
||||
if len(logs) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -32,7 +44,7 @@ func (r *OperationLogRepository) BatchCreateOperationLogs(ctx context.Context, l
|
||||
}
|
||||
|
||||
// GetOperationLogs 获取操作日志列表(分页)
|
||||
func (r *OperationLogRepository) GetOperationLogs(ctx context.Context, filters LogFilter, page, pageSize int) ([]*model.OperationLog, int64, error) {
|
||||
func (r *operationLogRepository) GetOperationLogs(ctx context.Context, filters dto.LogFilter, page, pageSize int) ([]*model.OperationLog, int64, error) {
|
||||
query := r.db.WithContext(ctx).Model(&model.OperationLog{})
|
||||
|
||||
if filters.UserID != "" {
|
||||
@@ -75,22 +87,22 @@ func (r *OperationLogRepository) GetOperationLogs(ctx context.Context, filters L
|
||||
}
|
||||
|
||||
// GetOperationLogsByUser 获取指定用户的操作日志
|
||||
func (r *OperationLogRepository) GetOperationLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.OperationLog, int64, error) {
|
||||
return r.GetOperationLogs(ctx, LogFilter{UserID: userID}, page, pageSize)
|
||||
func (r *operationLogRepository) GetOperationLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.OperationLog, int64, error) {
|
||||
return r.GetOperationLogs(ctx, dto.LogFilter{UserID: userID}, page, pageSize)
|
||||
}
|
||||
|
||||
// GetOperationLogsByTimeRange 按时间范围获取操作日志
|
||||
func (r *OperationLogRepository) GetOperationLogsByTimeRange(ctx context.Context, startTime, endTime string, page, pageSize int) ([]*model.OperationLog, int64, error) {
|
||||
return r.GetOperationLogs(ctx, LogFilter{StartTime: startTime, EndTime: endTime}, page, pageSize)
|
||||
func (r *operationLogRepository) GetOperationLogsByTimeRange(ctx context.Context, startTime, endTime string, page, pageSize int) ([]*model.OperationLog, int64, error) {
|
||||
return r.GetOperationLogs(ctx, dto.LogFilter{StartTime: startTime, EndTime: endTime}, page, pageSize)
|
||||
}
|
||||
|
||||
// DeleteOldLogs 删除旧的操作日志(用于定时清理)
|
||||
func (r *OperationLogRepository) DeleteOldLogs(ctx context.Context, beforeDate string) error {
|
||||
func (r *operationLogRepository) DeleteOldLogs(ctx context.Context, beforeDate string) error {
|
||||
return r.db.WithContext(ctx).Where("created_at < ?", beforeDate).Delete(&model.OperationLog{}).Error
|
||||
}
|
||||
|
||||
// GetStatistics 获取操作日志统计(使用单次 GROUP BY 查询,避免多次 COUNT)
|
||||
func (r *OperationLogRepository) GetStatistics(ctx context.Context, startTime, endTime string) (*OperationLogStatistics, error) {
|
||||
func (r *operationLogRepository) GetStatistics(ctx context.Context, startTime, endTime string) (*OperationLogStatistics, error) {
|
||||
stats := &OperationLogStatistics{}
|
||||
|
||||
query := r.db.WithContext(ctx).Model(&model.OperationLog{})
|
||||
@@ -125,18 +137,6 @@ func (r *OperationLogRepository) GetStatistics(ctx context.Context, startTime, e
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// LogFilter 日志过滤条件
|
||||
type LogFilter struct {
|
||||
UserID string
|
||||
Operation string
|
||||
TargetType string
|
||||
TargetID string
|
||||
Status string
|
||||
IP string
|
||||
StartTime string
|
||||
EndTime string
|
||||
}
|
||||
|
||||
// OperationLogStatistics 操作日志统计
|
||||
type OperationLogStatistics struct {
|
||||
TotalCount int64
|
||||
|
||||
@@ -11,18 +11,63 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// PostRepository 帖子仓储
|
||||
type PostRepository struct {
|
||||
// PostRepository 帖子仓储接口
|
||||
type PostRepository interface {
|
||||
Create(post *model.Post, images []string) error
|
||||
GetByID(id string) (*model.Post, error)
|
||||
Update(post *model.Post) error
|
||||
UpdateWithImages(post *model.Post, images *[]string) error
|
||||
UpdateModerationStatus(postID string, status model.PostStatus, rejectReason string, reviewedBy string) error
|
||||
Delete(id string) error
|
||||
List(page, pageSize int, userID string, includePending bool, channelID *string) ([]*model.Post, int64, error)
|
||||
GetUserPosts(userID string, page, pageSize int, includePending bool) ([]*model.Post, int64, error)
|
||||
GetFavorites(userID string, page, pageSize int) ([]*model.Post, int64, error)
|
||||
Like(postID, userID string) error
|
||||
Unlike(postID, userID string) error
|
||||
IsLiked(postID, userID string) bool
|
||||
IsLikedBatch(postIDs []string, userID string) map[string]bool
|
||||
Favorite(postID, userID string) error
|
||||
Unfavorite(postID, userID string) error
|
||||
IsFavorited(postID, userID string) bool
|
||||
IsFavoritedBatch(postIDs []string, userID string) map[string]bool
|
||||
IncrementViews(postID string) error
|
||||
IncrementShares(postID string) (int, error)
|
||||
Search(keyword string, page, pageSize int) ([]*model.Post, int64, error)
|
||||
GetFollowingPosts(userID string, page, pageSize int) ([]*model.Post, int64, error)
|
||||
GetHotPosts(page, pageSize int) ([]*model.Post, int64, error)
|
||||
ListPublishedPostIDsSince(since time.Time, limit int) ([]string, error)
|
||||
ListPinnedPublishedPostIDs() ([]string, error)
|
||||
GetPostHotSnapshotsByIDs(ids []string) ([]PostHotSnapshot, error)
|
||||
GetByIDs(ids []string) ([]*model.Post, error)
|
||||
CreateWithContext(ctx context.Context, post *model.Post, images []string) error
|
||||
GetByIDWithContext(ctx context.Context, id string) (*model.Post, error)
|
||||
UpdateWithContext(ctx context.Context, post *model.Post) error
|
||||
DeleteWithContext(ctx context.Context, id string) error
|
||||
AdminList(query AdminPostListQuery) ([]*model.Post, int64, error)
|
||||
GetByIDForAdmin(id string) (*model.Post, error)
|
||||
BatchDelete(ids []string) ([]string, error)
|
||||
BatchUpdateStatus(ids []string, status model.PostStatus, reviewedBy string) ([]string, error)
|
||||
UpdatePinStatus(postID string, isPinned bool) error
|
||||
UpdateFeatureStatus(postID string, isFeatured bool) error
|
||||
GetPostsByCursor(ctx context.Context, userID string, includePending bool, channelID *string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
|
||||
SearchPostsByCursor(ctx context.Context, keyword string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
|
||||
GetUserPostsByCursor(ctx context.Context, userID string, includePending bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
|
||||
GetFollowingPostsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
|
||||
GetHotPostsByCursor(ctx context.Context, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
|
||||
}
|
||||
|
||||
// postRepository 帖子仓储实现
|
||||
type postRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewPostRepository 创建帖子仓储
|
||||
func NewPostRepository(db *gorm.DB) *PostRepository {
|
||||
return &PostRepository{db: db}
|
||||
func NewPostRepository(db *gorm.DB) PostRepository {
|
||||
return &postRepository{db: db}
|
||||
}
|
||||
|
||||
// getDB 获取数据库连接(优先使用 context 中的事务)
|
||||
func (r *PostRepository) getDB(ctx context.Context) *gorm.DB {
|
||||
func (r *postRepository) getDB(ctx context.Context) *gorm.DB {
|
||||
if tx := GetTxFromContext(ctx); tx != nil {
|
||||
return tx
|
||||
}
|
||||
@@ -30,7 +75,7 @@ func (r *PostRepository) getDB(ctx context.Context) *gorm.DB {
|
||||
}
|
||||
|
||||
// Create 创建帖子
|
||||
func (r *PostRepository) Create(post *model.Post, images []string) error {
|
||||
func (r *postRepository) Create(post *model.Post, images []string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 创建帖子
|
||||
if err := tx.Create(post).Error; err != nil {
|
||||
@@ -68,7 +113,7 @@ func (r *PostRepository) Create(post *model.Post, images []string) error {
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取帖子
|
||||
func (r *PostRepository) GetByID(id string) (*model.Post, error) {
|
||||
func (r *postRepository) GetByID(id string) (*model.Post, error) {
|
||||
var post model.Post
|
||||
err := r.db.Preload("User").Preload("Images").First(&post, "id = ?", id).Error
|
||||
if err != nil {
|
||||
@@ -78,7 +123,7 @@ func (r *PostRepository) GetByID(id string) (*model.Post, error) {
|
||||
}
|
||||
|
||||
// Update 更新帖子
|
||||
func (r *PostRepository) Update(post *model.Post) error {
|
||||
func (r *postRepository) Update(post *model.Post) error {
|
||||
now := time.Now()
|
||||
post.UpdatedAt = now
|
||||
post.ContentEditedAt = &now
|
||||
@@ -86,7 +131,7 @@ func (r *PostRepository) Update(post *model.Post) error {
|
||||
}
|
||||
|
||||
// UpdateWithImages 更新帖子及其图片(images=nil 表示不更新图片)
|
||||
func (r *PostRepository) UpdateWithImages(post *model.Post, images *[]string) error {
|
||||
func (r *postRepository) UpdateWithImages(post *model.Post, images *[]string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
now := time.Now()
|
||||
post.UpdatedAt = now
|
||||
@@ -119,7 +164,7 @@ func (r *PostRepository) UpdateWithImages(post *model.Post, images *[]string) er
|
||||
}
|
||||
|
||||
// UpdateModerationStatus 更新帖子审核状态
|
||||
func (r *PostRepository) UpdateModerationStatus(postID string, status model.PostStatus, rejectReason string, reviewedBy string) error {
|
||||
func (r *postRepository) UpdateModerationStatus(postID string, status model.PostStatus, rejectReason string, reviewedBy string) error {
|
||||
// 审核状态更新属于管理操作,不应影响帖子内容更新时间(updated_at)
|
||||
updates := map[string]interface{}{
|
||||
"status": status,
|
||||
@@ -132,7 +177,7 @@ func (r *PostRepository) UpdateModerationStatus(postID string, status model.Post
|
||||
}
|
||||
|
||||
// Delete 删除帖子(软删除,同时清理关联数据)
|
||||
func (r *PostRepository) Delete(id string) error {
|
||||
func (r *postRepository) Delete(id string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 删除帖子图片
|
||||
if err := tx.Where("post_id = ?", id).Delete(&model.PostImage{}).Error; err != nil {
|
||||
@@ -166,7 +211,7 @@ func (r *PostRepository) Delete(id string) error {
|
||||
|
||||
// List 分页获取帖子列表
|
||||
// includePending=true 时,仅在指定 userID 下额外返回 pending(用于作者查看自己待审核帖子)
|
||||
func (r *PostRepository) List(page, pageSize int, userID string, includePending bool, channelID *string) ([]*model.Post, int64, error) {
|
||||
func (r *postRepository) List(page, pageSize int, userID string, includePending bool, channelID *string) ([]*model.Post, int64, error) {
|
||||
var posts []*model.Post
|
||||
var total int64
|
||||
|
||||
@@ -196,7 +241,7 @@ func (r *PostRepository) List(page, pageSize int, userID string, includePending
|
||||
}
|
||||
|
||||
// GetUserPosts 获取用户帖子
|
||||
func (r *PostRepository) GetUserPosts(userID string, page, pageSize int, includePending bool) ([]*model.Post, int64, error) {
|
||||
func (r *postRepository) GetUserPosts(userID string, page, pageSize int, includePending bool) ([]*model.Post, int64, error) {
|
||||
var posts []*model.Post
|
||||
var total int64
|
||||
|
||||
@@ -227,7 +272,7 @@ func (r *PostRepository) GetUserPosts(userID string, page, pageSize int, include
|
||||
}
|
||||
|
||||
// GetFavorites 获取用户收藏
|
||||
func (r *PostRepository) GetFavorites(userID string, page, pageSize int) ([]*model.Post, int64, error) {
|
||||
func (r *postRepository) GetFavorites(userID string, page, pageSize int) ([]*model.Post, int64, error) {
|
||||
var posts []*model.Post
|
||||
var total int64
|
||||
|
||||
@@ -241,7 +286,7 @@ func (r *PostRepository) GetFavorites(userID string, page, pageSize int) ([]*mod
|
||||
}
|
||||
|
||||
// Like 点赞帖子
|
||||
func (r *PostRepository) Like(postID, userID string) error {
|
||||
func (r *postRepository) Like(postID, userID string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 检查是否已经点赞
|
||||
var existing model.PostLike
|
||||
@@ -272,7 +317,7 @@ func (r *PostRepository) Like(postID, userID string) error {
|
||||
}
|
||||
|
||||
// Unlike 取消点赞
|
||||
func (r *PostRepository) Unlike(postID, userID string) error {
|
||||
func (r *postRepository) Unlike(postID, userID string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
result := tx.Where("post_id = ? AND user_id = ?", postID, userID).Delete(&model.PostLike{})
|
||||
if result.Error != nil {
|
||||
@@ -290,7 +335,7 @@ func (r *PostRepository) Unlike(postID, userID string) error {
|
||||
}
|
||||
|
||||
// IsLiked 检查是否点赞
|
||||
func (r *PostRepository) IsLiked(postID, userID string) bool {
|
||||
func (r *postRepository) IsLiked(postID, userID string) bool {
|
||||
var count int64
|
||||
r.db.Model(&model.PostLike{}).Where("post_id = ? AND user_id = ?", postID, userID).Count(&count)
|
||||
return count > 0
|
||||
@@ -298,7 +343,7 @@ func (r *PostRepository) IsLiked(postID, userID string) bool {
|
||||
|
||||
// IsLikedBatch 批量检查是否点赞(解决 N+1 问题)
|
||||
// 返回 map[postID]bool
|
||||
func (r *PostRepository) IsLikedBatch(postIDs []string, userID string) map[string]bool {
|
||||
func (r *postRepository) IsLikedBatch(postIDs []string, userID string) map[string]bool {
|
||||
result := make(map[string]bool)
|
||||
if len(postIDs) == 0 || userID == "" {
|
||||
return result
|
||||
@@ -324,7 +369,7 @@ func (r *PostRepository) IsLikedBatch(postIDs []string, userID string) map[strin
|
||||
}
|
||||
|
||||
// Favorite 收藏帖子
|
||||
func (r *PostRepository) Favorite(postID, userID string) error {
|
||||
func (r *postRepository) Favorite(postID, userID string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 检查是否已经收藏
|
||||
var existing model.Favorite
|
||||
@@ -355,7 +400,7 @@ func (r *PostRepository) Favorite(postID, userID string) error {
|
||||
}
|
||||
|
||||
// Unfavorite 取消收藏
|
||||
func (r *PostRepository) Unfavorite(postID, userID string) error {
|
||||
func (r *postRepository) Unfavorite(postID, userID string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
result := tx.Where("post_id = ? AND user_id = ?", postID, userID).Delete(&model.Favorite{})
|
||||
if result.Error != nil {
|
||||
@@ -374,7 +419,7 @@ func (r *PostRepository) Unfavorite(postID, userID string) error {
|
||||
}
|
||||
|
||||
// IsFavorited 检查是否收藏
|
||||
func (r *PostRepository) IsFavorited(postID, userID string) bool {
|
||||
func (r *postRepository) IsFavorited(postID, userID string) bool {
|
||||
var count int64
|
||||
r.db.Model(&model.Favorite{}).Where("post_id = ? AND user_id = ?", postID, userID).Count(&count)
|
||||
return count > 0
|
||||
@@ -382,7 +427,7 @@ func (r *PostRepository) IsFavorited(postID, userID string) bool {
|
||||
|
||||
// IsFavoritedBatch 批量检查是否收藏(解决 N+1 问题)
|
||||
// 返回 map[postID]bool
|
||||
func (r *PostRepository) IsFavoritedBatch(postIDs []string, userID string) map[string]bool {
|
||||
func (r *postRepository) IsFavoritedBatch(postIDs []string, userID string) map[string]bool {
|
||||
result := make(map[string]bool)
|
||||
if len(postIDs) == 0 || userID == "" {
|
||||
return result
|
||||
@@ -408,7 +453,7 @@ func (r *PostRepository) IsFavoritedBatch(postIDs []string, userID string) map[s
|
||||
}
|
||||
|
||||
// IncrementViews 增加帖子观看量
|
||||
func (r *PostRepository) IncrementViews(postID string) error {
|
||||
func (r *postRepository) IncrementViews(postID string) error {
|
||||
return r.db.Model(&model.Post{}).Where("id = ?", postID).
|
||||
UpdateColumns(map[string]interface{}{
|
||||
"views_count": gorm.Expr("views_count + 1"),
|
||||
@@ -417,7 +462,7 @@ func (r *PostRepository) IncrementViews(postID string) error {
|
||||
}
|
||||
|
||||
// IncrementShares 已发布帖子分享次数 +1,返回新的 shares_count;非已发布或不存在返回 gorm.ErrRecordNotFound
|
||||
func (r *PostRepository) IncrementShares(postID string) (int, error) {
|
||||
func (r *postRepository) IncrementShares(postID string) (int, error) {
|
||||
var newCount int
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
res := tx.Model(&model.Post{}).
|
||||
@@ -443,7 +488,7 @@ func (r *PostRepository) IncrementShares(postID string) (int, error) {
|
||||
}
|
||||
|
||||
// Search 搜索帖子
|
||||
func (r *PostRepository) Search(keyword string, page, pageSize int) ([]*model.Post, int64, error) {
|
||||
func (r *postRepository) Search(keyword string, page, pageSize int) ([]*model.Post, int64, error) {
|
||||
var posts []*model.Post
|
||||
var total int64
|
||||
|
||||
@@ -472,7 +517,7 @@ func (r *PostRepository) Search(keyword string, page, pageSize int) ([]*model.Po
|
||||
}
|
||||
|
||||
// GetFollowingPosts 获取关注用户的帖子
|
||||
func (r *PostRepository) GetFollowingPosts(userID string, page, pageSize int) ([]*model.Post, int64, error) {
|
||||
func (r *postRepository) GetFollowingPosts(userID string, page, pageSize int) ([]*model.Post, int64, error) {
|
||||
var posts []*model.Post
|
||||
var total int64
|
||||
|
||||
@@ -493,7 +538,7 @@ func (r *PostRepository) GetFollowingPosts(userID string, page, pageSize int) ([
|
||||
}
|
||||
|
||||
// GetHotPosts 热门榜降级:Redis 未就绪时按最新发布排序
|
||||
func (r *PostRepository) GetHotPosts(page, pageSize int) ([]*model.Post, int64, error) {
|
||||
func (r *postRepository) GetHotPosts(page, pageSize int) ([]*model.Post, int64, error) {
|
||||
var posts []*model.Post
|
||||
var total int64
|
||||
|
||||
@@ -520,7 +565,7 @@ type PostHotSnapshot struct {
|
||||
}
|
||||
|
||||
// ListPublishedPostIDsSince 按发布时间倒序取 ID(热门候选池,避免全表扫快照)
|
||||
func (r *PostRepository) ListPublishedPostIDsSince(since time.Time, limit int) ([]string, error) {
|
||||
func (r *postRepository) ListPublishedPostIDsSince(since time.Time, limit int) ([]string, error) {
|
||||
if limit <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -547,7 +592,7 @@ func (r *PostRepository) ListPublishedPostIDsSince(since time.Time, limit int) (
|
||||
}
|
||||
|
||||
// ListPinnedPublishedPostIDs 已发布且置顶的帖子 ID(越新发布的置顶越靠前)
|
||||
func (r *PostRepository) ListPinnedPublishedPostIDs() ([]string, error) {
|
||||
func (r *postRepository) ListPinnedPublishedPostIDs() ([]string, error) {
|
||||
type row struct {
|
||||
ID string `gorm:"column:id"`
|
||||
}
|
||||
@@ -570,7 +615,7 @@ func (r *PostRepository) ListPinnedPublishedPostIDs() ([]string, error) {
|
||||
}
|
||||
|
||||
// GetPostHotSnapshotsByIDs 仅拉取候选 ID 对应的热度字段(分批 IN 查询)
|
||||
func (r *PostRepository) GetPostHotSnapshotsByIDs(ids []string) ([]PostHotSnapshot, error) {
|
||||
func (r *postRepository) GetPostHotSnapshotsByIDs(ids []string) ([]PostHotSnapshot, error) {
|
||||
if len(ids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -596,7 +641,7 @@ func (r *PostRepository) GetPostHotSnapshotsByIDs(ids []string) ([]PostHotSnapsh
|
||||
}
|
||||
|
||||
// GetByIDs 根据ID列表获取帖子(保持传入顺序)
|
||||
func (r *PostRepository) GetByIDs(ids []string) ([]*model.Post, error) {
|
||||
func (r *postRepository) GetByIDs(ids []string) ([]*model.Post, error) {
|
||||
if len(ids) == 0 {
|
||||
return []*model.Post{}, nil
|
||||
}
|
||||
@@ -628,7 +673,7 @@ func (r *PostRepository) GetByIDs(ids []string) ([]*model.Post, error) {
|
||||
// ========== Context-aware methods for transaction support ==========
|
||||
|
||||
// CreateWithContext 创建帖子(支持事务)
|
||||
func (r *PostRepository) CreateWithContext(ctx context.Context, post *model.Post, images []string) error {
|
||||
func (r *postRepository) CreateWithContext(ctx context.Context, post *model.Post, images []string) error {
|
||||
db := r.getDB(ctx)
|
||||
|
||||
// 如果 context 中有事务,直接使用
|
||||
@@ -643,7 +688,7 @@ func (r *PostRepository) CreateWithContext(ctx context.Context, post *model.Post
|
||||
}
|
||||
|
||||
// createWithTx 使用指定事务创建帖子
|
||||
func (r *PostRepository) createWithTx(tx *gorm.DB, post *model.Post, images []string) error {
|
||||
func (r *postRepository) createWithTx(tx *gorm.DB, post *model.Post, images []string) error {
|
||||
// 创建帖子
|
||||
if err := tx.Create(post).Error; err != nil {
|
||||
return err
|
||||
@@ -665,7 +710,7 @@ func (r *PostRepository) createWithTx(tx *gorm.DB, post *model.Post, images []st
|
||||
}
|
||||
|
||||
// GetByIDWithContext 根据ID获取帖子(支持事务)
|
||||
func (r *PostRepository) GetByIDWithContext(ctx context.Context, id string) (*model.Post, error) {
|
||||
func (r *postRepository) GetByIDWithContext(ctx context.Context, id string) (*model.Post, error) {
|
||||
var post model.Post
|
||||
err := r.getDB(ctx).WithContext(ctx).Preload("User").Preload("Images").First(&post, "id = ?", id).Error
|
||||
if err != nil {
|
||||
@@ -675,7 +720,7 @@ func (r *PostRepository) GetByIDWithContext(ctx context.Context, id string) (*mo
|
||||
}
|
||||
|
||||
// UpdateWithContext 更新帖子(支持事务)
|
||||
func (r *PostRepository) UpdateWithContext(ctx context.Context, post *model.Post) error {
|
||||
func (r *postRepository) UpdateWithContext(ctx context.Context, post *model.Post) error {
|
||||
now := time.Now()
|
||||
post.UpdatedAt = now
|
||||
post.ContentEditedAt = &now
|
||||
@@ -683,7 +728,7 @@ func (r *PostRepository) UpdateWithContext(ctx context.Context, post *model.Post
|
||||
}
|
||||
|
||||
// DeleteWithContext 删除帖子(支持事务)
|
||||
func (r *PostRepository) DeleteWithContext(ctx context.Context, id string) error {
|
||||
func (r *postRepository) DeleteWithContext(ctx context.Context, id string) error {
|
||||
db := r.getDB(ctx)
|
||||
|
||||
// 如果 context 中有事务,直接使用
|
||||
@@ -698,7 +743,7 @@ func (r *PostRepository) DeleteWithContext(ctx context.Context, id string) error
|
||||
}
|
||||
|
||||
// deleteWithTx 使用指定事务删除帖子
|
||||
func (r *PostRepository) deleteWithTx(tx *gorm.DB, id string) error {
|
||||
func (r *postRepository) deleteWithTx(tx *gorm.DB, id string) error {
|
||||
// 删除帖子图片
|
||||
if err := tx.Where("post_id = ?", id).Delete(&model.PostImage{}).Error; err != nil {
|
||||
return err
|
||||
@@ -742,7 +787,7 @@ type AdminPostListQuery struct {
|
||||
}
|
||||
|
||||
// AdminList 管理端分页获取帖子列表(支持多条件筛选)
|
||||
func (r *PostRepository) AdminList(query AdminPostListQuery) ([]*model.Post, int64, error) {
|
||||
func (r *postRepository) AdminList(query AdminPostListQuery) ([]*model.Post, int64, error) {
|
||||
var posts []*model.Post
|
||||
var total int64
|
||||
|
||||
@@ -793,7 +838,7 @@ func (r *PostRepository) AdminList(query AdminPostListQuery) ([]*model.Post, int
|
||||
}
|
||||
|
||||
// GetByIDForAdmin 管理端根据ID获取帖子(包含所有状态)
|
||||
func (r *PostRepository) GetByIDForAdmin(id string) (*model.Post, error) {
|
||||
func (r *postRepository) GetByIDForAdmin(id string) (*model.Post, error) {
|
||||
var post model.Post
|
||||
err := r.db.Preload("User").Preload("Images").First(&post, "id = ?", id).Error
|
||||
if err != nil {
|
||||
@@ -803,7 +848,7 @@ func (r *PostRepository) GetByIDForAdmin(id string) (*model.Post, error) {
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除帖子(使用单次事务批量删除,避免 N+1 问题)
|
||||
func (r *PostRepository) BatchDelete(ids []string) ([]string, error) {
|
||||
func (r *postRepository) BatchDelete(ids []string) ([]string, error) {
|
||||
if len(ids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -855,7 +900,7 @@ func (r *PostRepository) BatchDelete(ids []string) ([]string, error) {
|
||||
}
|
||||
|
||||
// BatchUpdateStatus 批量更新帖子审核状态(使用单条 SQL 批量更新,避免 N+1 问题)
|
||||
func (r *PostRepository) BatchUpdateStatus(ids []string, status model.PostStatus, reviewedBy string) ([]string, error) {
|
||||
func (r *postRepository) BatchUpdateStatus(ids []string, status model.PostStatus, reviewedBy string) ([]string, error) {
|
||||
if len(ids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -900,7 +945,7 @@ func (r *PostRepository) BatchUpdateStatus(ids []string, status model.PostStatus
|
||||
}
|
||||
|
||||
// UpdatePinStatus 更新帖子置顶状态
|
||||
func (r *PostRepository) UpdatePinStatus(postID string, isPinned bool) error {
|
||||
func (r *postRepository) UpdatePinStatus(postID string, isPinned bool) error {
|
||||
// 置顶状态属于管理操作,不应影响帖子内容更新时间(updated_at)
|
||||
return r.db.Model(&model.Post{}).Where("id = ?", postID).
|
||||
UpdateColumns(map[string]any{
|
||||
@@ -910,7 +955,7 @@ func (r *PostRepository) UpdatePinStatus(postID string, isPinned bool) error {
|
||||
}
|
||||
|
||||
// UpdateFeatureStatus 更新帖子加精状态
|
||||
func (r *PostRepository) UpdateFeatureStatus(postID string, isFeatured bool) error {
|
||||
func (r *postRepository) UpdateFeatureStatus(postID string, isFeatured bool) error {
|
||||
// 加精状态属于管理操作,不应影响帖子内容更新时间(updated_at)
|
||||
return r.db.Model(&model.Post{}).Where("id = ?", postID).
|
||||
UpdateColumns(map[string]any{
|
||||
@@ -923,7 +968,7 @@ func (r *PostRepository) UpdateFeatureStatus(postID string, isFeatured bool) err
|
||||
|
||||
// GetPostsByCursor 游标分页获取帖子列表
|
||||
// includePending=true 时,仅在指定 userID 下额外返回 pending(用于作者查看自己待审核帖子)
|
||||
func (r *PostRepository) GetPostsByCursor(ctx context.Context, userID string, includePending bool, channelID *string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
|
||||
func (r *postRepository) GetPostsByCursor(ctx context.Context, userID string, includePending bool, channelID *string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
|
||||
db := r.getDB(ctx).WithContext(ctx)
|
||||
|
||||
// 构建基础查询
|
||||
@@ -993,7 +1038,7 @@ func (r *PostRepository) GetPostsByCursor(ctx context.Context, userID string, in
|
||||
}
|
||||
|
||||
// SearchPostsByCursor 游标分页搜索帖子
|
||||
func (r *PostRepository) SearchPostsByCursor(ctx context.Context, keyword string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
|
||||
func (r *postRepository) SearchPostsByCursor(ctx context.Context, keyword string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
|
||||
db := r.getDB(ctx).WithContext(ctx)
|
||||
|
||||
query := db.Model(&model.Post{}).Where("status = ?", model.PostStatusPublished)
|
||||
@@ -1059,7 +1104,7 @@ func (r *PostRepository) SearchPostsByCursor(ctx context.Context, keyword string
|
||||
}
|
||||
|
||||
// GetUserPostsByCursor 游标分页获取用户帖子
|
||||
func (r *PostRepository) GetUserPostsByCursor(ctx context.Context, userID string, includePending bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
|
||||
func (r *postRepository) GetUserPostsByCursor(ctx context.Context, userID string, includePending bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
|
||||
db := r.getDB(ctx).WithContext(ctx)
|
||||
|
||||
query := db.Model(&model.Post{}).Where("user_id = ?", userID)
|
||||
@@ -1119,7 +1164,7 @@ func (r *PostRepository) GetUserPostsByCursor(ctx context.Context, userID string
|
||||
}
|
||||
|
||||
// GetFollowingPostsByCursor 游标分页获取关注用户的帖子
|
||||
func (r *PostRepository) GetFollowingPostsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
|
||||
func (r *postRepository) GetFollowingPostsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
|
||||
db := r.getDB(ctx).WithContext(ctx)
|
||||
|
||||
// 子查询:获取当前用户关注的所有用户ID
|
||||
@@ -1175,7 +1220,7 @@ func (r *PostRepository) GetFollowingPostsByCursor(ctx context.Context, userID s
|
||||
}
|
||||
|
||||
// GetHotPostsByCursor 游标分页获取热门帖子
|
||||
func (r *PostRepository) GetHotPostsByCursor(ctx context.Context, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
|
||||
func (r *postRepository) GetHotPostsByCursor(ctx context.Context, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
|
||||
db := r.getDB(ctx).WithContext(ctx)
|
||||
|
||||
// 热门游标降级:与 GetHotPosts 一致,按最新发布排序
|
||||
|
||||
@@ -8,23 +8,41 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// PushRecordRepository 推送记录仓储
|
||||
type PushRecordRepository struct {
|
||||
// PushRecordRepository 推送记录仓储接口
|
||||
type PushRecordRepository interface {
|
||||
Create(record *model.PushRecord) error
|
||||
GetByID(id int64) (*model.PushRecord, error)
|
||||
Update(record *model.PushRecord) error
|
||||
GetPendingPushes(limit int) ([]*model.PushRecord, error)
|
||||
GetByUserID(userID string, limit, offset int) ([]*model.PushRecord, error)
|
||||
GetByMessageID(messageID int64) ([]*model.PushRecord, error)
|
||||
GetFailedPushesForRetry(limit int) ([]*model.PushRecord, error)
|
||||
BatchCreate(records []*model.PushRecord) error
|
||||
BatchUpdateStatus(ids []int64, status model.PushStatus) error
|
||||
UpdateStatus(id int64, status model.PushStatus) error
|
||||
MarkAsFailed(id int64, errMsg string) error
|
||||
MarkAsDelivered(id int64) error
|
||||
DeleteExpiredRecords() error
|
||||
GetStatsByUserID(userID int64) (map[model.PushStatus]int64, error)
|
||||
}
|
||||
|
||||
// pushRecordRepository 推送记录仓储实现
|
||||
type pushRecordRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewPushRecordRepository 创建推送记录仓储
|
||||
func NewPushRecordRepository(db *gorm.DB) *PushRecordRepository {
|
||||
return &PushRecordRepository{db: db}
|
||||
func NewPushRecordRepository(db *gorm.DB) PushRecordRepository {
|
||||
return &pushRecordRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建推送记录
|
||||
func (r *PushRecordRepository) Create(record *model.PushRecord) error {
|
||||
func (r *pushRecordRepository) Create(record *model.PushRecord) error {
|
||||
return r.db.Create(record).Error
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取推送记录
|
||||
func (r *PushRecordRepository) GetByID(id int64) (*model.PushRecord, error) {
|
||||
func (r *pushRecordRepository) GetByID(id int64) (*model.PushRecord, error) {
|
||||
var record model.PushRecord
|
||||
err := r.db.First(&record, "id = ?", id).Error
|
||||
if err != nil {
|
||||
@@ -34,12 +52,12 @@ func (r *PushRecordRepository) GetByID(id int64) (*model.PushRecord, error) {
|
||||
}
|
||||
|
||||
// Update 更新推送记录
|
||||
func (r *PushRecordRepository) Update(record *model.PushRecord) error {
|
||||
func (r *pushRecordRepository) Update(record *model.PushRecord) error {
|
||||
return r.db.Save(record).Error
|
||||
}
|
||||
|
||||
// GetPendingPushes 获取待推送记录
|
||||
func (r *PushRecordRepository) GetPendingPushes(limit int) ([]*model.PushRecord, error) {
|
||||
func (r *pushRecordRepository) GetPendingPushes(limit int) ([]*model.PushRecord, error) {
|
||||
var records []*model.PushRecord
|
||||
err := r.db.Where("push_status = ?", model.PushStatusPending).
|
||||
Where("expired_at IS NULL OR expired_at > ?", time.Now()).
|
||||
@@ -51,7 +69,7 @@ func (r *PushRecordRepository) GetPendingPushes(limit int) ([]*model.PushRecord,
|
||||
|
||||
// GetByUserID 根据用户ID获取推送记录
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (r *PushRecordRepository) GetByUserID(userID string, limit, offset int) ([]*model.PushRecord, error) {
|
||||
func (r *pushRecordRepository) GetByUserID(userID string, limit, offset int) ([]*model.PushRecord, error) {
|
||||
var records []*model.PushRecord
|
||||
err := r.db.Where("user_id = ?", userID).
|
||||
Order("created_at DESC").
|
||||
@@ -62,7 +80,7 @@ func (r *PushRecordRepository) GetByUserID(userID string, limit, offset int) ([]
|
||||
}
|
||||
|
||||
// GetByMessageID 根据消息ID获取推送记录
|
||||
func (r *PushRecordRepository) GetByMessageID(messageID int64) ([]*model.PushRecord, error) {
|
||||
func (r *pushRecordRepository) GetByMessageID(messageID int64) ([]*model.PushRecord, error) {
|
||||
var records []*model.PushRecord
|
||||
err := r.db.Where("message_id = ?", messageID).
|
||||
Order("created_at DESC").
|
||||
@@ -71,7 +89,7 @@ func (r *PushRecordRepository) GetByMessageID(messageID int64) ([]*model.PushRec
|
||||
}
|
||||
|
||||
// GetFailedPushesForRetry 获取失败待重试的推送
|
||||
func (r *PushRecordRepository) GetFailedPushesForRetry(limit int) ([]*model.PushRecord, error) {
|
||||
func (r *pushRecordRepository) GetFailedPushesForRetry(limit int) ([]*model.PushRecord, error) {
|
||||
var records []*model.PushRecord
|
||||
err := r.db.Where("push_status = ?", model.PushStatusFailed).
|
||||
Where("retry_count < max_retry").
|
||||
@@ -83,7 +101,7 @@ func (r *PushRecordRepository) GetFailedPushesForRetry(limit int) ([]*model.Push
|
||||
}
|
||||
|
||||
// BatchCreate 批量创建推送记录
|
||||
func (r *PushRecordRepository) BatchCreate(records []*model.PushRecord) error {
|
||||
func (r *pushRecordRepository) BatchCreate(records []*model.PushRecord) error {
|
||||
if len(records) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -91,7 +109,7 @@ func (r *PushRecordRepository) BatchCreate(records []*model.PushRecord) error {
|
||||
}
|
||||
|
||||
// BatchUpdateStatus 批量更新推送状态
|
||||
func (r *PushRecordRepository) BatchUpdateStatus(ids []int64, status model.PushStatus) error {
|
||||
func (r *pushRecordRepository) BatchUpdateStatus(ids []int64, status model.PushStatus) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -107,7 +125,7 @@ func (r *PushRecordRepository) BatchUpdateStatus(ids []int64, status model.PushS
|
||||
}
|
||||
|
||||
// UpdateStatus 更新单条记录状态
|
||||
func (r *PushRecordRepository) UpdateStatus(id int64, status model.PushStatus) error {
|
||||
func (r *pushRecordRepository) UpdateStatus(id int64, status model.PushStatus) error {
|
||||
updates := map[string]interface{}{
|
||||
"push_status": status,
|
||||
}
|
||||
@@ -120,7 +138,7 @@ func (r *PushRecordRepository) UpdateStatus(id int64, status model.PushStatus) e
|
||||
}
|
||||
|
||||
// MarkAsFailed 标记为失败
|
||||
func (r *PushRecordRepository) MarkAsFailed(id int64, errMsg string) error {
|
||||
func (r *pushRecordRepository) MarkAsFailed(id int64, errMsg string) error {
|
||||
return r.db.Model(&model.PushRecord{}).
|
||||
Where("id = ?", id).
|
||||
Updates(map[string]interface{}{
|
||||
@@ -131,7 +149,7 @@ func (r *PushRecordRepository) MarkAsFailed(id int64, errMsg string) error {
|
||||
}
|
||||
|
||||
// MarkAsDelivered 标记为已送达
|
||||
func (r *PushRecordRepository) MarkAsDelivered(id int64) error {
|
||||
func (r *pushRecordRepository) MarkAsDelivered(id int64) error {
|
||||
return r.db.Model(&model.PushRecord{}).
|
||||
Where("id = ?", id).
|
||||
Updates(map[string]interface{}{
|
||||
@@ -141,13 +159,13 @@ func (r *PushRecordRepository) MarkAsDelivered(id int64) error {
|
||||
}
|
||||
|
||||
// DeleteExpiredRecords 删除过期的推送记录(软删除)
|
||||
func (r *PushRecordRepository) DeleteExpiredRecords() error {
|
||||
func (r *pushRecordRepository) DeleteExpiredRecords() error {
|
||||
return r.db.Where("expired_at IS NOT NULL AND expired_at < ?", time.Now()).
|
||||
Delete(&model.PushRecord{}).Error
|
||||
}
|
||||
|
||||
// GetStatsByUserID 获取用户推送统计
|
||||
func (r *PushRecordRepository) GetStatsByUserID(userID int64) (map[model.PushStatus]int64, error) {
|
||||
func (r *pushRecordRepository) GetStatsByUserID(userID int64) (map[model.PushStatus]int64, error) {
|
||||
type statusCount struct {
|
||||
Status model.PushStatus
|
||||
Count int64
|
||||
|
||||
@@ -10,23 +10,38 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// SystemNotificationRepository 系统通知仓储
|
||||
type SystemNotificationRepository struct {
|
||||
// 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 {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewSystemNotificationRepository 创建系统通知仓储
|
||||
func NewSystemNotificationRepository(db *gorm.DB) *SystemNotificationRepository {
|
||||
return &SystemNotificationRepository{db: db}
|
||||
func NewSystemNotificationRepository(db *gorm.DB) SystemNotificationRepository {
|
||||
return &systemNotificationRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建系统通知
|
||||
func (r *SystemNotificationRepository) Create(notification *model.SystemNotification) error {
|
||||
func (r *systemNotificationRepository) Create(notification *model.SystemNotification) error {
|
||||
return r.db.Create(notification).Error
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取通知
|
||||
func (r *SystemNotificationRepository) GetByID(id int64) (*model.SystemNotification, error) {
|
||||
func (r *systemNotificationRepository) GetByID(id int64) (*model.SystemNotification, error) {
|
||||
var notification model.SystemNotification
|
||||
err := r.db.First(¬ification, "id = ?", id).Error
|
||||
if err != nil {
|
||||
@@ -36,7 +51,7 @@ func (r *SystemNotificationRepository) GetByID(id int64) (*model.SystemNotificat
|
||||
}
|
||||
|
||||
// GetByReceiverID 获取用户的通知列表
|
||||
func (r *SystemNotificationRepository) GetByReceiverID(receiverID string, page, pageSize int) ([]*model.SystemNotification, int64, error) {
|
||||
func (r *systemNotificationRepository) GetByReceiverID(receiverID string, page, pageSize int) ([]*model.SystemNotification, int64, error) {
|
||||
var notifications []*model.SystemNotification
|
||||
var total int64
|
||||
|
||||
@@ -53,7 +68,7 @@ func (r *SystemNotificationRepository) GetByReceiverID(receiverID string, page,
|
||||
}
|
||||
|
||||
// GetUnreadByReceiverID 获取用户的未读通知列表
|
||||
func (r *SystemNotificationRepository) GetUnreadByReceiverID(receiverID string, limit int) ([]*model.SystemNotification, error) {
|
||||
func (r *systemNotificationRepository) GetUnreadByReceiverID(receiverID string, limit int) ([]*model.SystemNotification, error) {
|
||||
var notifications []*model.SystemNotification
|
||||
err := r.db.Where("receiver_id = ? AND is_read = ?", receiverID, false).
|
||||
Order("created_at DESC").
|
||||
@@ -63,7 +78,7 @@ func (r *SystemNotificationRepository) GetUnreadByReceiverID(receiverID string,
|
||||
}
|
||||
|
||||
// GetUnreadCount 获取用户未读通知数量
|
||||
func (r *SystemNotificationRepository) GetUnreadCount(receiverID string) (int64, error) {
|
||||
func (r *systemNotificationRepository) GetUnreadCount(receiverID string) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.SystemNotification{}).
|
||||
Where("receiver_id = ? AND is_read = ?", receiverID, false).
|
||||
@@ -72,7 +87,7 @@ func (r *SystemNotificationRepository) GetUnreadCount(receiverID string) (int64,
|
||||
}
|
||||
|
||||
// MarkAsRead 标记单条通知为已读
|
||||
func (r *SystemNotificationRepository) MarkAsRead(id int64, receiverID string) error {
|
||||
func (r *systemNotificationRepository) MarkAsRead(id int64, receiverID string) error {
|
||||
now := model.SystemNotification{}.UpdatedAt
|
||||
return r.db.Model(&model.SystemNotification{}).
|
||||
Where("id = ? AND receiver_id = ?", id, receiverID).
|
||||
@@ -83,7 +98,7 @@ func (r *SystemNotificationRepository) MarkAsRead(id int64, receiverID string) e
|
||||
}
|
||||
|
||||
// MarkAllAsRead 标记用户所有通知为已读
|
||||
func (r *SystemNotificationRepository) MarkAllAsRead(receiverID string) error {
|
||||
func (r *systemNotificationRepository) MarkAllAsRead(receiverID string) error {
|
||||
now := model.SystemNotification{}.UpdatedAt
|
||||
return r.db.Model(&model.SystemNotification{}).
|
||||
Where("receiver_id = ? AND is_read = ?", receiverID, false).
|
||||
@@ -94,13 +109,13 @@ func (r *SystemNotificationRepository) MarkAllAsRead(receiverID string) error {
|
||||
}
|
||||
|
||||
// Delete 删除通知(软删除)
|
||||
func (r *SystemNotificationRepository) Delete(id int64, receiverID string) error {
|
||||
func (r *systemNotificationRepository) Delete(id int64, receiverID string) error {
|
||||
return r.db.Where("id = ? AND receiver_id = ?", id, receiverID).
|
||||
Delete(&model.SystemNotification{}).Error
|
||||
}
|
||||
|
||||
// GetByType 获取用户指定类型的通知
|
||||
func (r *SystemNotificationRepository) GetByType(receiverID string, notifyType model.SystemNotificationType, page, pageSize int) ([]*model.SystemNotification, int64, error) {
|
||||
func (r *systemNotificationRepository) GetByType(receiverID string, notifyType model.SystemNotificationType, page, pageSize int) ([]*model.SystemNotification, int64, error) {
|
||||
var notifications []*model.SystemNotification
|
||||
var total int64
|
||||
|
||||
@@ -119,7 +134,7 @@ func (r *SystemNotificationRepository) GetByType(receiverID string, notifyType m
|
||||
|
||||
// GetByReceiverIDCursor 游标分页获取用户的通知列表
|
||||
// 使用统一的 cursor 包进行游标分页
|
||||
func (r *SystemNotificationRepository) GetByReceiverIDCursor(ctx context.Context, receiverID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.SystemNotification], error) {
|
||||
func (r *systemNotificationRepository) GetByReceiverIDCursor(ctx context.Context, receiverID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.SystemNotification], error) {
|
||||
// 构建基础查询
|
||||
query := r.db.WithContext(ctx).Model(&model.SystemNotification{}).Where("receiver_id = ?", receiverID)
|
||||
|
||||
@@ -165,7 +180,7 @@ func (r *SystemNotificationRepository) GetByReceiverIDCursor(ctx context.Context
|
||||
|
||||
// GetByReceiverIDCursorLegacy 游标分页获取用户的通知列表(旧版兼容接口)
|
||||
// 已废弃:请使用 GetByReceiverIDCursor
|
||||
func (r *SystemNotificationRepository) GetByReceiverIDCursorLegacy(receiverID string, cursorStr string, pageSize int) ([]*model.SystemNotification, string, bool, error) {
|
||||
func (r *systemNotificationRepository) GetByReceiverIDCursorLegacy(receiverID string, cursorStr string, pageSize int) ([]*model.SystemNotification, string, bool, error) {
|
||||
// 构建基础查询
|
||||
query := r.db.Model(&model.SystemNotification{}).Where("receiver_id = ?", receiverID)
|
||||
|
||||
|
||||
@@ -36,21 +36,36 @@ type activityCache interface {
|
||||
Expire(ctx context.Context, key string, ttl time.Duration) error
|
||||
}
|
||||
|
||||
// UserActivityRepository 用户活跃数据仓储
|
||||
type UserActivityRepository struct {
|
||||
// UserActivityRepository 用户活跃数据仓储接口
|
||||
type UserActivityRepository interface {
|
||||
RecordActivity(ctx context.Context, userID, loginType, ip, userAgent string) error
|
||||
GetDAU(ctx context.Context, date time.Time) (int64, error)
|
||||
GetActiveUserIDs(ctx context.Context, date time.Time) ([]string, error)
|
||||
GetUserActivityCount(ctx context.Context, userID string, startDate, endDate time.Time) (int64, error)
|
||||
SaveStat(ctx context.Context, stat *model.UserActivityStat) error
|
||||
GetStat(ctx context.Context, statType string, date time.Time) (*model.UserActivityStat, error)
|
||||
RecordActivityToRedis(ctx context.Context, userID string) error
|
||||
GetDAUFromRedis(ctx context.Context, date time.Time) (int64, error)
|
||||
GetWAUFromRedis(ctx context.Context, year int, week int) (int64, error)
|
||||
GetMAUFromRedis(ctx context.Context, year int, month int) (int64, error)
|
||||
GetDAUUserIDsFromRedis(ctx context.Context, date time.Time) ([]string, error)
|
||||
}
|
||||
|
||||
// userActivityRepository 用户活跃数据仓储实现
|
||||
type userActivityRepository struct {
|
||||
db *gorm.DB
|
||||
cache activityCache
|
||||
}
|
||||
|
||||
// NewUserActivityRepository 创建用户活跃数据仓储
|
||||
func NewUserActivityRepository(db *gorm.DB, cache activityCache) *UserActivityRepository {
|
||||
return &UserActivityRepository{db: db, cache: cache}
|
||||
func NewUserActivityRepository(db *gorm.DB, cache activityCache) UserActivityRepository {
|
||||
return &userActivityRepository{db: db, cache: cache}
|
||||
}
|
||||
|
||||
// ==================== 数据库操作方法 ====================
|
||||
|
||||
// RecordActivity 记录用户活跃(写入数据库)
|
||||
func (r *UserActivityRepository) RecordActivity(ctx context.Context, userID, loginType, ip, userAgent string) error {
|
||||
func (r *userActivityRepository) RecordActivity(ctx context.Context, userID, loginType, ip, userAgent string) error {
|
||||
log := &model.UserActiveLog{
|
||||
UserID: userID,
|
||||
ActiveDate: time.Now(),
|
||||
@@ -62,7 +77,7 @@ func (r *UserActivityRepository) RecordActivity(ctx context.Context, userID, log
|
||||
}
|
||||
|
||||
// GetDAU 获取指定日期的日活用户数(从数据库)
|
||||
func (r *UserActivityRepository) GetDAU(ctx context.Context, date time.Time) (int64, error) {
|
||||
func (r *userActivityRepository) GetDAU(ctx context.Context, date time.Time) (int64, error) {
|
||||
var count int64
|
||||
startOfDay := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
|
||||
endOfDay := startOfDay.Add(24 * time.Hour)
|
||||
@@ -77,7 +92,7 @@ func (r *UserActivityRepository) GetDAU(ctx context.Context, date time.Time) (in
|
||||
}
|
||||
|
||||
// GetActiveUserIDs 获取指定日期的活跃用户ID列表
|
||||
func (r *UserActivityRepository) GetActiveUserIDs(ctx context.Context, date time.Time) ([]string, error) {
|
||||
func (r *userActivityRepository) GetActiveUserIDs(ctx context.Context, date time.Time) ([]string, error) {
|
||||
var userIDs []string
|
||||
startOfDay := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
|
||||
endOfDay := startOfDay.Add(24 * time.Hour)
|
||||
@@ -92,7 +107,7 @@ func (r *UserActivityRepository) GetActiveUserIDs(ctx context.Context, date time
|
||||
}
|
||||
|
||||
// GetUserActivityCount 获取用户在指定日期范围内的活跃天数
|
||||
func (r *UserActivityRepository) GetUserActivityCount(ctx context.Context, userID string, startDate, endDate time.Time) (int64, error) {
|
||||
func (r *userActivityRepository) GetUserActivityCount(ctx context.Context, userID string, startDate, endDate time.Time) (int64, error) {
|
||||
var count int64
|
||||
startOfDay := time.Date(startDate.Year(), startDate.Month(), startDate.Day(), 0, 0, 0, 0, startDate.Location())
|
||||
endOfDay := time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 23, 59, 59, 0, endDate.Location())
|
||||
@@ -107,7 +122,7 @@ func (r *UserActivityRepository) GetUserActivityCount(ctx context.Context, userI
|
||||
}
|
||||
|
||||
// SaveStat 保存统计数据快照
|
||||
func (r *UserActivityRepository) SaveStat(ctx context.Context, stat *model.UserActivityStat) error {
|
||||
func (r *userActivityRepository) SaveStat(ctx context.Context, stat *model.UserActivityStat) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "stat_type"}, {Name: "stat_date"}},
|
||||
@@ -117,7 +132,7 @@ func (r *UserActivityRepository) SaveStat(ctx context.Context, stat *model.UserA
|
||||
}
|
||||
|
||||
// GetStat 获取统计数据快照
|
||||
func (r *UserActivityRepository) GetStat(ctx context.Context, statType string, date time.Time) (*model.UserActivityStat, error) {
|
||||
func (r *userActivityRepository) GetStat(ctx context.Context, statType string, date time.Time) (*model.UserActivityStat, error) {
|
||||
var stat model.UserActivityStat
|
||||
startOfDay := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
|
||||
|
||||
@@ -134,7 +149,7 @@ func (r *UserActivityRepository) GetStat(ctx context.Context, statType string, d
|
||||
|
||||
// RecordActivityToRedis 记录用户活跃到 Redis Sorted Set
|
||||
// score 为时间戳,member 为用户ID
|
||||
func (r *UserActivityRepository) RecordActivityToRedis(ctx context.Context, userID string) error {
|
||||
func (r *userActivityRepository) RecordActivityToRedis(ctx context.Context, userID string) error {
|
||||
now := time.Now()
|
||||
score := float64(now.Unix())
|
||||
|
||||
@@ -164,7 +179,7 @@ func (r *UserActivityRepository) RecordActivityToRedis(ctx context.Context, user
|
||||
}
|
||||
|
||||
// GetDAUFromRedis 从 Redis 获取日活用户数
|
||||
func (r *UserActivityRepository) GetDAUFromRedis(ctx context.Context, date time.Time) (int64, error) {
|
||||
func (r *userActivityRepository) GetDAUFromRedis(ctx context.Context, date time.Time) (int64, error) {
|
||||
key := formatDAUKey(date)
|
||||
count, err := r.cache.ZCard(ctx, key)
|
||||
if err != nil {
|
||||
@@ -174,7 +189,7 @@ func (r *UserActivityRepository) GetDAUFromRedis(ctx context.Context, date time.
|
||||
}
|
||||
|
||||
// GetWAUFromRedis 从 Redis 获取周活用户数
|
||||
func (r *UserActivityRepository) GetWAUFromRedis(ctx context.Context, year int, week int) (int64, error) {
|
||||
func (r *userActivityRepository) GetWAUFromRedis(ctx context.Context, year int, week int) (int64, error) {
|
||||
key := formatWAUKey(year, week)
|
||||
count, err := r.cache.ZCard(ctx, key)
|
||||
if err != nil {
|
||||
@@ -184,7 +199,7 @@ func (r *UserActivityRepository) GetWAUFromRedis(ctx context.Context, year int,
|
||||
}
|
||||
|
||||
// GetMAUFromRedis 从 Redis 获取月活用户数
|
||||
func (r *UserActivityRepository) GetMAUFromRedis(ctx context.Context, year int, month int) (int64, error) {
|
||||
func (r *userActivityRepository) GetMAUFromRedis(ctx context.Context, year int, month int) (int64, error) {
|
||||
key := formatMAUKey(year, month)
|
||||
count, err := r.cache.ZCard(ctx, key)
|
||||
if err != nil {
|
||||
@@ -194,7 +209,7 @@ func (r *UserActivityRepository) GetMAUFromRedis(ctx context.Context, year int,
|
||||
}
|
||||
|
||||
// GetDAUUserIDsFromRedis 获取日活用户ID列表
|
||||
func (r *UserActivityRepository) GetDAUUserIDsFromRedis(ctx context.Context, date time.Time) ([]string, error) {
|
||||
func (r *userActivityRepository) GetDAUUserIDsFromRedis(ctx context.Context, date time.Time) ([]string, error) {
|
||||
key := formatDAUKey(date)
|
||||
// 获取所有成员(从最小分数到最大分数)
|
||||
members, err := r.cache.ZRangeByScore(ctx, key, "-inf", "+inf", 0, -1)
|
||||
|
||||
@@ -8,23 +8,60 @@ import (
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
// UserRepository 用户仓储
|
||||
type UserRepository struct {
|
||||
// UserRepository 用户仓储接口
|
||||
type UserRepository interface {
|
||||
Create(user *model.User) error
|
||||
GetByID(id string) (*model.User, error)
|
||||
GetByUsername(username string) (*model.User, error)
|
||||
GetByEmail(email string) (*model.User, error)
|
||||
GetByPhone(phone string) (*model.User, error)
|
||||
Update(user *model.User) error
|
||||
Delete(id string) error
|
||||
List(page, pageSize int) ([]*model.User, int64, error)
|
||||
GetFollowers(userID string, page, pageSize int) ([]*model.User, int64, error)
|
||||
GetFollowing(userID string, page, pageSize int) ([]*model.User, int64, error)
|
||||
CreateFollow(follow *model.Follow) error
|
||||
DeleteFollow(followerID, followingID string) error
|
||||
IsFollowing(followerID, followingID string) (bool, error)
|
||||
IsFollowingBatch(followerID string, targetUserIDs []string) (map[string]bool, error)
|
||||
IncrementFollowersCount(userID string) error
|
||||
DecrementFollowersCount(userID string) error
|
||||
IncrementFollowingCount(userID string) error
|
||||
DecrementFollowingCount(userID string) error
|
||||
RefreshFollowersCount(userID string) error
|
||||
GetPostsCount(userID string) (int64, error)
|
||||
GetPostsCountBatch(userIDs []string) (map[string]int64, error)
|
||||
RefreshFollowingCount(userID string) error
|
||||
IsBlocked(blockerID, blockedID string) (bool, error)
|
||||
IsBlockedBatch(blockerID string, blockedIDs []string) (map[string]bool, error)
|
||||
IsBlockedEitherDirection(userA, userB string) (bool, error)
|
||||
BlockUserAndCleanupRelations(blockerID, blockedID string) error
|
||||
UnblockUser(blockerID, blockedID string) error
|
||||
GetBlockedUsers(blockerID string, page, pageSize int) ([]*model.User, int64, error)
|
||||
Search(keyword string, page, pageSize int) ([]*model.User, int64, error)
|
||||
GetMutualFollowStatus(currentUserID string, targetUserIDs []string) (map[string][2]bool, error)
|
||||
AdminList(page, pageSize int, keyword, status string) ([]*model.User, int64, error)
|
||||
GetCommentsCount(userID string) (int64, error)
|
||||
UpdateStatus(userID string, status model.UserStatus) error
|
||||
}
|
||||
|
||||
// userRepository 用户仓储实现
|
||||
type userRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewUserRepository 创建用户仓储
|
||||
func NewUserRepository(db *gorm.DB) *UserRepository {
|
||||
return &UserRepository{db: db}
|
||||
func NewUserRepository(db *gorm.DB) UserRepository {
|
||||
return &userRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建用户
|
||||
func (r *UserRepository) Create(user *model.User) error {
|
||||
func (r *userRepository) Create(user *model.User) error {
|
||||
return r.db.Create(user).Error
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取用户
|
||||
func (r *UserRepository) GetByID(id string) (*model.User, error) {
|
||||
func (r *userRepository) GetByID(id string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.db.First(&user, "id = ?", id).Error
|
||||
if err != nil {
|
||||
@@ -34,7 +71,7 @@ func (r *UserRepository) GetByID(id string) (*model.User, error) {
|
||||
}
|
||||
|
||||
// GetByUsername 根据用户名获取用户
|
||||
func (r *UserRepository) GetByUsername(username string) (*model.User, error) {
|
||||
func (r *userRepository) GetByUsername(username string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.db.First(&user, "username = ?", username).Error
|
||||
if err != nil {
|
||||
@@ -44,7 +81,7 @@ func (r *UserRepository) GetByUsername(username string) (*model.User, error) {
|
||||
}
|
||||
|
||||
// GetByEmail 根据邮箱获取用户
|
||||
func (r *UserRepository) GetByEmail(email string) (*model.User, error) {
|
||||
func (r *userRepository) GetByEmail(email string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.db.First(&user, "email = ?", email).Error
|
||||
if err != nil {
|
||||
@@ -54,7 +91,7 @@ func (r *UserRepository) GetByEmail(email string) (*model.User, error) {
|
||||
}
|
||||
|
||||
// GetByPhone 根据手机号获取用户
|
||||
func (r *UserRepository) GetByPhone(phone string) (*model.User, error) {
|
||||
func (r *userRepository) GetByPhone(phone string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.db.First(&user, "phone = ?", phone).Error
|
||||
if err != nil {
|
||||
@@ -64,17 +101,17 @@ func (r *UserRepository) GetByPhone(phone string) (*model.User, error) {
|
||||
}
|
||||
|
||||
// Update 更新用户
|
||||
func (r *UserRepository) Update(user *model.User) error {
|
||||
func (r *userRepository) Update(user *model.User) error {
|
||||
return r.db.Save(user).Error
|
||||
}
|
||||
|
||||
// Delete 删除用户
|
||||
func (r *UserRepository) Delete(id string) error {
|
||||
func (r *userRepository) Delete(id string) error {
|
||||
return r.db.Delete(&model.User{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
// List 分页获取用户列表
|
||||
func (r *UserRepository) List(page, pageSize int) ([]*model.User, int64, error) {
|
||||
func (r *userRepository) List(page, pageSize int) ([]*model.User, int64, error) {
|
||||
var users []*model.User
|
||||
var total int64
|
||||
|
||||
@@ -87,7 +124,7 @@ func (r *UserRepository) List(page, pageSize int) ([]*model.User, int64, error)
|
||||
}
|
||||
|
||||
// GetFollowers 获取用户粉丝
|
||||
func (r *UserRepository) GetFollowers(userID string, page, pageSize int) ([]*model.User, int64, error) {
|
||||
func (r *userRepository) GetFollowers(userID string, page, pageSize int) ([]*model.User, int64, error) {
|
||||
var users []*model.User
|
||||
var total int64
|
||||
|
||||
@@ -104,7 +141,7 @@ func (r *UserRepository) GetFollowers(userID string, page, pageSize int) ([]*mod
|
||||
}
|
||||
|
||||
// GetFollowing 获取用户关注
|
||||
func (r *UserRepository) GetFollowing(userID string, page, pageSize int) ([]*model.User, int64, error) {
|
||||
func (r *userRepository) GetFollowing(userID string, page, pageSize int) ([]*model.User, int64, error) {
|
||||
var users []*model.User
|
||||
var total int64
|
||||
|
||||
@@ -121,17 +158,17 @@ func (r *UserRepository) GetFollowing(userID string, page, pageSize int) ([]*mod
|
||||
}
|
||||
|
||||
// CreateFollow 创建关注关系
|
||||
func (r *UserRepository) CreateFollow(follow *model.Follow) error {
|
||||
func (r *userRepository) CreateFollow(follow *model.Follow) error {
|
||||
return r.db.Create(follow).Error
|
||||
}
|
||||
|
||||
// DeleteFollow 删除关注关系
|
||||
func (r *UserRepository) DeleteFollow(followerID, followingID string) error {
|
||||
func (r *userRepository) DeleteFollow(followerID, followingID string) error {
|
||||
return r.db.Where("follower_id = ? AND following_id = ?", followerID, followingID).Delete(&model.Follow{}).Error
|
||||
}
|
||||
|
||||
// IsFollowing 检查是否关注了某用户
|
||||
func (r *UserRepository) IsFollowing(followerID, followingID string) (bool, error) {
|
||||
func (r *userRepository) IsFollowing(followerID, followingID string) (bool, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.Follow{}).Where("follower_id = ? AND following_id = ?", followerID, followingID).Count(&count).Error
|
||||
if err != nil {
|
||||
@@ -142,7 +179,7 @@ func (r *UserRepository) IsFollowing(followerID, followingID string) (bool, erro
|
||||
|
||||
// IsFollowingBatch 批量检查当前用户是否关注了目标用户(解决 N+1 问题)
|
||||
// 返回 map[targetUserID]bool
|
||||
func (r *UserRepository) IsFollowingBatch(followerID string, targetUserIDs []string) (map[string]bool, error) {
|
||||
func (r *userRepository) IsFollowingBatch(followerID string, targetUserIDs []string) (map[string]bool, error) {
|
||||
result := make(map[string]bool)
|
||||
if len(targetUserIDs) == 0 || followerID == "" {
|
||||
return result, nil
|
||||
@@ -171,31 +208,31 @@ func (r *UserRepository) IsFollowingBatch(followerID string, targetUserIDs []str
|
||||
}
|
||||
|
||||
// IncrementFollowersCount 增加用户粉丝数
|
||||
func (r *UserRepository) IncrementFollowersCount(userID string) error {
|
||||
func (r *userRepository) IncrementFollowersCount(userID string) error {
|
||||
return r.db.Model(&model.User{}).Where("id = ?", userID).
|
||||
UpdateColumn("followers_count", gorm.Expr("followers_count + 1")).Error
|
||||
}
|
||||
|
||||
// DecrementFollowersCount 减少用户粉丝数
|
||||
func (r *UserRepository) DecrementFollowersCount(userID string) error {
|
||||
func (r *userRepository) DecrementFollowersCount(userID string) error {
|
||||
return r.db.Model(&model.User{}).Where("id = ? AND followers_count > 0", userID).
|
||||
UpdateColumn("followers_count", gorm.Expr("followers_count - 1")).Error
|
||||
}
|
||||
|
||||
// IncrementFollowingCount 增加用户关注数
|
||||
func (r *UserRepository) IncrementFollowingCount(userID string) error {
|
||||
func (r *userRepository) IncrementFollowingCount(userID string) error {
|
||||
return r.db.Model(&model.User{}).Where("id = ?", userID).
|
||||
UpdateColumn("following_count", gorm.Expr("following_count + 1")).Error
|
||||
}
|
||||
|
||||
// DecrementFollowingCount 减少用户关注数
|
||||
func (r *UserRepository) DecrementFollowingCount(userID string) error {
|
||||
func (r *userRepository) DecrementFollowingCount(userID string) error {
|
||||
return r.db.Model(&model.User{}).Where("id = ? AND following_count > 0", userID).
|
||||
UpdateColumn("following_count", gorm.Expr("following_count - 1")).Error
|
||||
}
|
||||
|
||||
// RefreshFollowersCount 刷新用户粉丝数(通过实际计数)
|
||||
func (r *UserRepository) RefreshFollowersCount(userID string) error {
|
||||
func (r *userRepository) RefreshFollowersCount(userID string) error {
|
||||
var count int64
|
||||
err := r.db.Model(&model.Follow{}).Where("following_id = ?", userID).Count(&count).Error
|
||||
if err != nil {
|
||||
@@ -206,7 +243,7 @@ func (r *UserRepository) RefreshFollowersCount(userID string) error {
|
||||
}
|
||||
|
||||
// GetPostsCount 获取用户帖子数(实时计算)
|
||||
func (r *UserRepository) GetPostsCount(userID string) (int64, error) {
|
||||
func (r *userRepository) GetPostsCount(userID string) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.Post{}).
|
||||
Where("user_id = ? AND status = ?", userID, model.PostStatusPublished).
|
||||
@@ -216,7 +253,7 @@ func (r *UserRepository) GetPostsCount(userID string) (int64, error) {
|
||||
|
||||
// GetPostsCountBatch 批量获取用户帖子数(实时计算)
|
||||
// 返回 map[userID]postsCount
|
||||
func (r *UserRepository) GetPostsCountBatch(userIDs []string) (map[string]int64, error) {
|
||||
func (r *userRepository) GetPostsCountBatch(userIDs []string) (map[string]int64, error) {
|
||||
result := make(map[string]int64)
|
||||
if len(userIDs) == 0 {
|
||||
return result, nil
|
||||
@@ -251,7 +288,7 @@ func (r *UserRepository) GetPostsCountBatch(userIDs []string) (map[string]int64,
|
||||
}
|
||||
|
||||
// RefreshFollowingCount 刷新用户关注数(通过实际计数)
|
||||
func (r *UserRepository) RefreshFollowingCount(userID string) error {
|
||||
func (r *userRepository) RefreshFollowingCount(userID string) error {
|
||||
var count int64
|
||||
err := r.db.Model(&model.Follow{}).Where("follower_id = ?", userID).Count(&count).Error
|
||||
if err != nil {
|
||||
@@ -262,7 +299,7 @@ func (r *UserRepository) RefreshFollowingCount(userID string) error {
|
||||
}
|
||||
|
||||
// IsBlocked 检查拉黑关系是否存在(blocker -> blocked)
|
||||
func (r *UserRepository) IsBlocked(blockerID, blockedID string) (bool, error) {
|
||||
func (r *userRepository) IsBlocked(blockerID, blockedID string) (bool, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.UserBlock{}).
|
||||
Where("blocker_id = ? AND blocked_id = ?", blockerID, blockedID).
|
||||
@@ -275,7 +312,7 @@ func (r *UserRepository) IsBlocked(blockerID, blockedID string) (bool, error) {
|
||||
|
||||
// IsBlockedBatch 批量检查拉黑关系(解决 N+1 问题)
|
||||
// 返回 map[blockedUserID]bool
|
||||
func (r *UserRepository) IsBlockedBatch(blockerID string, blockedIDs []string) (map[string]bool, error) {
|
||||
func (r *userRepository) IsBlockedBatch(blockerID string, blockedIDs []string) (map[string]bool, error) {
|
||||
result := make(map[string]bool)
|
||||
if len(blockedIDs) == 0 || blockerID == "" {
|
||||
return result, nil
|
||||
@@ -304,7 +341,7 @@ func (r *UserRepository) IsBlockedBatch(blockerID string, blockedIDs []string) (
|
||||
}
|
||||
|
||||
// IsBlockedEitherDirection 检查是否任一方向存在拉黑
|
||||
func (r *UserRepository) IsBlockedEitherDirection(userA, userB string) (bool, error) {
|
||||
func (r *userRepository) IsBlockedEitherDirection(userA, userB string) (bool, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.UserBlock{}).
|
||||
Where("(blocker_id = ? AND blocked_id = ?) OR (blocker_id = ? AND blocked_id = ?)",
|
||||
@@ -317,7 +354,7 @@ func (r *UserRepository) IsBlockedEitherDirection(userA, userB string) (bool, er
|
||||
}
|
||||
|
||||
// BlockUserAndCleanupRelations 拉黑用户并清理双向关注关系(事务)
|
||||
func (r *UserRepository) BlockUserAndCleanupRelations(blockerID, blockedID string) error {
|
||||
func (r *userRepository) BlockUserAndCleanupRelations(blockerID, blockedID string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
block := &model.UserBlock{
|
||||
BlockerID: blockerID,
|
||||
@@ -364,13 +401,13 @@ func (r *UserRepository) BlockUserAndCleanupRelations(blockerID, blockedID strin
|
||||
}
|
||||
|
||||
// UnblockUser 取消拉黑
|
||||
func (r *UserRepository) UnblockUser(blockerID, blockedID string) error {
|
||||
func (r *userRepository) UnblockUser(blockerID, blockedID string) error {
|
||||
return r.db.Where("blocker_id = ? AND blocked_id = ?", blockerID, blockedID).
|
||||
Delete(&model.UserBlock{}).Error
|
||||
}
|
||||
|
||||
// GetBlockedUsers 获取用户黑名单列表
|
||||
func (r *UserRepository) GetBlockedUsers(blockerID string, page, pageSize int) ([]*model.User, int64, error) {
|
||||
func (r *userRepository) GetBlockedUsers(blockerID string, page, pageSize int) ([]*model.User, int64, error) {
|
||||
var users []*model.User
|
||||
var total int64
|
||||
|
||||
@@ -388,7 +425,7 @@ func (r *UserRepository) GetBlockedUsers(blockerID string, page, pageSize int) (
|
||||
}
|
||||
|
||||
// Search 搜索用户
|
||||
func (r *UserRepository) Search(keyword string, page, pageSize int) ([]*model.User, int64, error) {
|
||||
func (r *userRepository) Search(keyword string, page, pageSize int) ([]*model.User, int64, error) {
|
||||
var users []*model.User
|
||||
var total int64
|
||||
|
||||
@@ -421,7 +458,7 @@ func (r *UserRepository) Search(keyword string, page, pageSize int) ([]*model.Us
|
||||
|
||||
// GetMutualFollowStatus 批量获取双向关注状态
|
||||
// 返回 map[userID][isFollowing, isFollowingMe]
|
||||
func (r *UserRepository) GetMutualFollowStatus(currentUserID string, targetUserIDs []string) (map[string][2]bool, error) {
|
||||
func (r *userRepository) GetMutualFollowStatus(currentUserID string, targetUserIDs []string) (map[string][2]bool, error) {
|
||||
result := make(map[string][2]bool)
|
||||
|
||||
if len(targetUserIDs) == 0 {
|
||||
@@ -465,7 +502,7 @@ func (r *UserRepository) GetMutualFollowStatus(currentUserID string, targetUserI
|
||||
}
|
||||
|
||||
// AdminList 管理端分页获取用户列表(支持关键词和状态筛选)
|
||||
func (r *UserRepository) AdminList(page, pageSize int, keyword, status string) ([]*model.User, int64, error) {
|
||||
func (r *userRepository) AdminList(page, pageSize int, keyword, status string) ([]*model.User, int64, error) {
|
||||
var users []*model.User
|
||||
var total int64
|
||||
|
||||
@@ -501,7 +538,7 @@ func (r *UserRepository) AdminList(page, pageSize int, keyword, status string) (
|
||||
}
|
||||
|
||||
// GetCommentsCount 获取用户评论数
|
||||
func (r *UserRepository) GetCommentsCount(userID string) (int64, error) {
|
||||
func (r *userRepository) GetCommentsCount(userID string) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.Comment{}).
|
||||
Where("user_id = ?", userID).
|
||||
@@ -510,6 +547,6 @@ func (r *UserRepository) GetCommentsCount(userID string) (int64, error) {
|
||||
}
|
||||
|
||||
// UpdateStatus 更新用户状态
|
||||
func (r *UserRepository) UpdateStatus(userID string, status model.UserStatus) error {
|
||||
func (r *userRepository) UpdateStatus(userID string, status model.UserStatus) error {
|
||||
return r.db.Model(&model.User{}).Where("id = ?", userID).Update("status", status).Error
|
||||
}
|
||||
|
||||
@@ -7,18 +7,29 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// VoteRepository 投票仓储
|
||||
type VoteRepository struct {
|
||||
// VoteRepository 投票仓储接口
|
||||
type VoteRepository interface {
|
||||
CreateOptions(postID string, options []string) error
|
||||
GetOptionsByPostID(postID string) ([]model.VoteOption, error)
|
||||
Vote(postID, userID, optionID string) error
|
||||
Unvote(postID, userID string) error
|
||||
GetUserVote(postID, userID string) (*model.UserVote, error)
|
||||
UpdateOption(optionID, content string) error
|
||||
DeleteOptionsByPostID(postID string) error
|
||||
}
|
||||
|
||||
// voteRepository 投票仓储实现
|
||||
type voteRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewVoteRepository 创建投票仓储
|
||||
func NewVoteRepository(db *gorm.DB) *VoteRepository {
|
||||
return &VoteRepository{db: db}
|
||||
func NewVoteRepository(db *gorm.DB) VoteRepository {
|
||||
return &voteRepository{db: db}
|
||||
}
|
||||
|
||||
// CreateOptions 批量创建投票选项(使用批量插入,避免 N+1 问题)
|
||||
func (r *VoteRepository) CreateOptions(postID string, options []string) error {
|
||||
func (r *voteRepository) CreateOptions(postID string, options []string) error {
|
||||
if len(options) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -38,14 +49,14 @@ func (r *VoteRepository) CreateOptions(postID string, options []string) error {
|
||||
}
|
||||
|
||||
// GetOptionsByPostID 获取帖子的所有投票选项
|
||||
func (r *VoteRepository) GetOptionsByPostID(postID string) ([]model.VoteOption, error) {
|
||||
func (r *voteRepository) GetOptionsByPostID(postID string) ([]model.VoteOption, error) {
|
||||
var options []model.VoteOption
|
||||
err := r.db.Where("post_id = ?", postID).Order("sort_order ASC").Find(&options).Error
|
||||
return options, err
|
||||
}
|
||||
|
||||
// Vote 用户投票
|
||||
func (r *VoteRepository) Vote(postID, userID, optionID string) error {
|
||||
func (r *voteRepository) Vote(postID, userID, optionID string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 检查用户是否已投票
|
||||
var existing model.UserVote
|
||||
@@ -83,7 +94,7 @@ func (r *VoteRepository) Vote(postID, userID, optionID string) error {
|
||||
}
|
||||
|
||||
// Unvote 取消投票
|
||||
func (r *VoteRepository) Unvote(postID, userID string) error {
|
||||
func (r *voteRepository) Unvote(postID, userID string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 获取用户的投票记录
|
||||
var userVote model.UserVote
|
||||
@@ -112,7 +123,7 @@ func (r *VoteRepository) Unvote(postID, userID string) error {
|
||||
}
|
||||
|
||||
// GetUserVote 获取用户在指定帖子的投票
|
||||
func (r *VoteRepository) GetUserVote(postID, userID string) (*model.UserVote, error) {
|
||||
func (r *voteRepository) GetUserVote(postID, userID string) (*model.UserVote, error) {
|
||||
var userVote model.UserVote
|
||||
err := r.db.Where("post_id = ? AND user_id = ?", postID, userID).First(&userVote).Error
|
||||
if err != nil {
|
||||
@@ -125,13 +136,13 @@ func (r *VoteRepository) GetUserVote(postID, userID string) (*model.UserVote, er
|
||||
}
|
||||
|
||||
// UpdateOption 更新选项内容
|
||||
func (r *VoteRepository) UpdateOption(optionID, content string) error {
|
||||
func (r *voteRepository) UpdateOption(optionID, content string) error {
|
||||
return r.db.Model(&model.VoteOption{}).Where("id = ?", optionID).
|
||||
Update("content", content).Error
|
||||
}
|
||||
|
||||
// DeleteOptionsByPostID 删除帖子的所有投票选项
|
||||
func (r *VoteRepository) DeleteOptionsByPostID(postID string) error {
|
||||
func (r *voteRepository) DeleteOptionsByPostID(postID string) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 删除关联的用户投票记录
|
||||
if err := tx.Where("post_id = ?", postID).Delete(&model.UserVote{}).Error; err != nil {
|
||||
|
||||
@@ -25,12 +25,12 @@ type AdminCommentService interface {
|
||||
|
||||
// adminCommentServiceImpl 管理端评论服务实现
|
||||
type adminCommentServiceImpl struct {
|
||||
commentRepo *repository.CommentRepository
|
||||
postRepo *repository.PostRepository
|
||||
commentRepo repository.CommentRepository
|
||||
postRepo repository.PostRepository
|
||||
}
|
||||
|
||||
// NewAdminCommentService 创建管理端评论服务
|
||||
func NewAdminCommentService(commentRepo *repository.CommentRepository, postRepo *repository.PostRepository) AdminCommentService {
|
||||
func NewAdminCommentService(commentRepo repository.CommentRepository, postRepo repository.PostRepository) AdminCommentService {
|
||||
return &adminCommentServiceImpl{
|
||||
commentRepo: commentRepo,
|
||||
postRepo: postRepo,
|
||||
|
||||
@@ -28,21 +28,21 @@ type AdminDashboardService interface {
|
||||
// adminDashboardServiceImpl 管理端仪表盘服务实现
|
||||
type adminDashboardServiceImpl struct {
|
||||
db *gorm.DB
|
||||
userRepo *repository.UserRepository
|
||||
postRepo *repository.PostRepository
|
||||
commentRepo *repository.CommentRepository
|
||||
userRepo repository.UserRepository
|
||||
postRepo repository.PostRepository
|
||||
commentRepo repository.CommentRepository
|
||||
groupRepo repository.GroupRepository
|
||||
activityRepo *repository.UserActivityRepository
|
||||
activityRepo repository.UserActivityRepository
|
||||
}
|
||||
|
||||
// NewAdminDashboardService 创建管理端仪表盘服务
|
||||
func NewAdminDashboardService(
|
||||
db *gorm.DB,
|
||||
userRepo *repository.UserRepository,
|
||||
postRepo *repository.PostRepository,
|
||||
commentRepo *repository.CommentRepository,
|
||||
userRepo repository.UserRepository,
|
||||
postRepo repository.PostRepository,
|
||||
commentRepo repository.CommentRepository,
|
||||
groupRepo repository.GroupRepository,
|
||||
activityRepo *repository.UserActivityRepository,
|
||||
activityRepo repository.UserActivityRepository,
|
||||
) AdminDashboardService {
|
||||
return &adminDashboardServiceImpl{
|
||||
db: db,
|
||||
|
||||
@@ -30,13 +30,13 @@ type AdminGroupService interface {
|
||||
// adminGroupServiceImpl 管理端群组服务实现
|
||||
type adminGroupServiceImpl struct {
|
||||
groupRepo repository.GroupRepository
|
||||
userRepo *repository.UserRepository
|
||||
userRepo repository.UserRepository
|
||||
}
|
||||
|
||||
// NewAdminGroupService 创建管理端群组服务
|
||||
func NewAdminGroupService(
|
||||
groupRepo repository.GroupRepository,
|
||||
userRepo *repository.UserRepository,
|
||||
userRepo repository.UserRepository,
|
||||
) AdminGroupService {
|
||||
return &adminGroupServiceImpl{
|
||||
groupRepo: groupRepo,
|
||||
|
||||
@@ -34,13 +34,13 @@ type AdminPostService interface {
|
||||
|
||||
// adminPostServiceImpl 管理端帖子服务实现
|
||||
type adminPostServiceImpl struct {
|
||||
postRepo *repository.PostRepository
|
||||
postRepo repository.PostRepository
|
||||
cache cache.Cache
|
||||
logService *LogService
|
||||
}
|
||||
|
||||
// NewAdminPostService 创建管理端帖子服务
|
||||
func NewAdminPostService(postRepo *repository.PostRepository, cacheBackend cache.Cache) AdminPostService {
|
||||
func NewAdminPostService(postRepo repository.PostRepository, cacheBackend cache.Cache) AdminPostService {
|
||||
return &adminPostServiceImpl{
|
||||
postRepo: postRepo,
|
||||
cache: cacheBackend,
|
||||
|
||||
@@ -20,11 +20,11 @@ type AdminUserService interface {
|
||||
|
||||
// adminUserServiceImpl 管理端用户服务实现
|
||||
type adminUserServiceImpl struct {
|
||||
userRepo *repository.UserRepository
|
||||
userRepo repository.UserRepository
|
||||
}
|
||||
|
||||
// NewAdminUserService 创建管理端用户服务
|
||||
func NewAdminUserService(userRepo *repository.UserRepository) AdminUserService {
|
||||
func NewAdminUserService(userRepo repository.UserRepository) AdminUserService {
|
||||
return &adminUserServiceImpl{
|
||||
userRepo: userRepo,
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"carrot_bbs/internal/repository"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AsyncLogConfig 异步日志配置
|
||||
@@ -31,7 +30,6 @@ type AsyncLogManager struct {
|
||||
dataChangeChan chan *model.DataChangeLog
|
||||
|
||||
cfg *AsyncLogConfig
|
||||
db *gorm.DB
|
||||
wg sync.WaitGroup
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
@@ -42,10 +40,9 @@ type AsyncLogManager struct {
|
||||
|
||||
// NewAsyncLogManager 创建异步日志管理器
|
||||
func NewAsyncLogManager(
|
||||
db *gorm.DB,
|
||||
operationRepo *repository.OperationLogRepository,
|
||||
loginRepo *repository.LoginLogRepository,
|
||||
dataChangeRepo *repository.DataChangeLogRepository,
|
||||
operationRepo repository.OperationLogRepository,
|
||||
loginRepo repository.LoginLogRepository,
|
||||
dataChangeRepo repository.DataChangeLogRepository,
|
||||
logger *zap.Logger,
|
||||
cfg *AsyncLogConfig,
|
||||
) *AsyncLogManager {
|
||||
@@ -56,7 +53,6 @@ func NewAsyncLogManager(
|
||||
loginChan: make(chan *model.LoginLog, cfg.BufferSize),
|
||||
dataChangeChan: make(chan *model.DataChangeLog, cfg.BufferSize),
|
||||
cfg: cfg,
|
||||
db: db,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
logger: logger,
|
||||
@@ -90,9 +86,9 @@ func NewAsyncLogManager(
|
||||
// worker 处理协程
|
||||
func (m *AsyncLogManager) worker(
|
||||
id int,
|
||||
operationRepo *repository.OperationLogRepository,
|
||||
loginRepo *repository.LoginLogRepository,
|
||||
dataChangeRepo *repository.DataChangeLogRepository,
|
||||
operationRepo repository.OperationLogRepository,
|
||||
loginRepo repository.LoginLogRepository,
|
||||
dataChangeRepo repository.DataChangeLogRepository,
|
||||
) {
|
||||
opBatch := make([]*model.OperationLog, 0, m.cfg.BatchSize)
|
||||
loginBatch := make([]*model.LoginLog, 0, m.cfg.BatchSize)
|
||||
@@ -164,9 +160,9 @@ func (m *AsyncLogManager) flushBatches(
|
||||
ops []*model.OperationLog,
|
||||
logins []*model.LoginLog,
|
||||
changes []*model.DataChangeLog,
|
||||
operationRepo *repository.OperationLogRepository,
|
||||
loginRepo *repository.LoginLogRepository,
|
||||
dataChangeRepo *repository.DataChangeLogRepository,
|
||||
operationRepo repository.OperationLogRepository,
|
||||
loginRepo repository.LoginLogRepository,
|
||||
dataChangeRepo repository.DataChangeLogRepository,
|
||||
) {
|
||||
if len(ops) == 0 && len(logins) == 0 && len(changes) == 0 {
|
||||
return
|
||||
@@ -194,17 +190,17 @@ func (m *AsyncLogManager) flushBatches(
|
||||
}
|
||||
|
||||
// flushOperationLogs 刷新操作日志
|
||||
func (m *AsyncLogManager) flushOperationLogs(logs []*model.OperationLog, repo *repository.OperationLogRepository) error {
|
||||
func (m *AsyncLogManager) flushOperationLogs(logs []*model.OperationLog, repo repository.OperationLogRepository) error {
|
||||
return repo.BatchCreateOperationLogs(context.Background(), logs)
|
||||
}
|
||||
|
||||
// flushLoginLogs 刷新登录日志
|
||||
func (m *AsyncLogManager) flushLoginLogs(logs []*model.LoginLog, repo *repository.LoginLogRepository) error {
|
||||
func (m *AsyncLogManager) flushLoginLogs(logs []*model.LoginLog, repo repository.LoginLogRepository) error {
|
||||
return repo.BatchCreateLoginLogs(context.Background(), logs)
|
||||
}
|
||||
|
||||
// flushDataChangeLogs 刷新数据变更日志
|
||||
func (m *AsyncLogManager) flushDataChangeLogs(logs []*model.DataChangeLog, repo *repository.DataChangeLogRepository) error {
|
||||
func (m *AsyncLogManager) flushDataChangeLogs(logs []*model.DataChangeLog, repo repository.DataChangeLogRepository) error {
|
||||
return repo.BatchCreateDataChangeLogs(context.Background(), logs)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@ type ChannelService interface {
|
||||
}
|
||||
|
||||
type channelServiceImpl struct {
|
||||
channelRepo *repository.ChannelRepository
|
||||
channelRepo repository.ChannelRepository
|
||||
cache cache.Cache
|
||||
}
|
||||
|
||||
func NewChannelService(channelRepo *repository.ChannelRepository, c cache.Cache) ChannelService {
|
||||
func NewChannelService(channelRepo repository.ChannelRepository, c cache.Cache) ChannelService {
|
||||
return &channelServiceImpl{channelRepo: channelRepo, cache: c}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,9 +56,8 @@ type ChatService interface {
|
||||
|
||||
// chatServiceImpl 聊天服务实现
|
||||
type chatServiceImpl struct {
|
||||
db *gorm.DB
|
||||
repo *repository.MessageRepository
|
||||
userRepo *repository.UserRepository
|
||||
repo repository.MessageRepository
|
||||
userRepo repository.UserRepository
|
||||
sensitive SensitiveService
|
||||
sseHub *sse.Hub
|
||||
|
||||
@@ -69,9 +68,8 @@ type chatServiceImpl struct {
|
||||
|
||||
// NewChatService 创建聊天服务
|
||||
func NewChatService(
|
||||
db *gorm.DB,
|
||||
repo *repository.MessageRepository,
|
||||
userRepo *repository.UserRepository,
|
||||
repo repository.MessageRepository,
|
||||
userRepo repository.UserRepository,
|
||||
sensitive SensitiveService,
|
||||
sseHub *sse.Hub,
|
||||
cacheBackend cache.Cache,
|
||||
@@ -90,7 +88,6 @@ func NewChatService(
|
||||
)
|
||||
|
||||
return &chatServiceImpl{
|
||||
db: db,
|
||||
repo: repo,
|
||||
userRepo: userRepo,
|
||||
sensitive: sensitive,
|
||||
@@ -538,13 +535,9 @@ func (s *chatServiceImpl) GetAllUnreadCount(ctx context.Context, userID string)
|
||||
// RecallMessage 撤回消息(2分钟内)
|
||||
func (s *chatServiceImpl) RecallMessage(ctx context.Context, messageID string, userID string) error {
|
||||
// 获取消息
|
||||
var message model.Message
|
||||
err := s.db.First(&message, "id = ?", messageID).Error
|
||||
message, err := s.repo.GetMessageByID(messageID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("message not found")
|
||||
}
|
||||
return fmt.Errorf("failed to get message: %w", err)
|
||||
return errors.New("message not found")
|
||||
}
|
||||
|
||||
// 验证是否是消息发送者
|
||||
@@ -563,10 +556,7 @@ func (s *chatServiceImpl) RecallMessage(ctx context.Context, messageID string, u
|
||||
}
|
||||
|
||||
// 更新消息状态为已撤回,并清空原始消息内容,仅保留撤回占位
|
||||
err = s.db.Model(&message).Updates(map[string]interface{}{
|
||||
"status": model.MessageStatusRecalled,
|
||||
"segments": model.MessageSegments{},
|
||||
}).Error
|
||||
err = s.repo.RecallMessage(messageID, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to recall message: %w", err)
|
||||
}
|
||||
@@ -604,22 +594,15 @@ func (s *chatServiceImpl) RecallMessage(ctx context.Context, messageID string, u
|
||||
// DeleteMessage 删除消息(仅对自己可见)
|
||||
func (s *chatServiceImpl) DeleteMessage(ctx context.Context, messageID string, userID string) error {
|
||||
// 获取消息
|
||||
var message model.Message
|
||||
err := s.db.First(&message, "id = ?", messageID).Error
|
||||
message, err := s.repo.GetMessageByID(messageID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("message not found")
|
||||
}
|
||||
return fmt.Errorf("failed to get message: %w", err)
|
||||
return errors.New("message not found")
|
||||
}
|
||||
|
||||
// 验证用户是否是会话参与者
|
||||
_, err = s.getParticipant(ctx, message.ConversationID, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("no permission to delete this message")
|
||||
}
|
||||
return fmt.Errorf("failed to get participant: %w", err)
|
||||
return errors.New("no permission to delete this message")
|
||||
}
|
||||
|
||||
// 对于删除消息,我们使用软删除,但需要确保只对当前用户隐藏
|
||||
@@ -629,7 +612,7 @@ func (s *chatServiceImpl) DeleteMessage(ctx context.Context, messageID string, u
|
||||
}
|
||||
|
||||
// 更新消息状态为已删除
|
||||
err = s.db.Model(&message).Update("status", model.MessageStatusDeleted).Error
|
||||
err = s.repo.UpdateMessageStatus(message.ID, model.MessageStatusDeleted)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete message: %w", err)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
)
|
||||
|
||||
type CommentService struct {
|
||||
commentRepo *repository.CommentRepository
|
||||
postRepo *repository.PostRepository
|
||||
commentRepo repository.CommentRepository
|
||||
postRepo repository.PostRepository
|
||||
systemMessageService SystemMessageService
|
||||
cache cache.Cache
|
||||
postAIService *PostAIService
|
||||
@@ -24,7 +24,7 @@ type CommentService struct {
|
||||
hookManager *hook.Manager
|
||||
}
|
||||
|
||||
func NewCommentService(commentRepo *repository.CommentRepository, postRepo *repository.PostRepository, systemMessageService SystemMessageService, postAIService *PostAIService, cacheBackend cache.Cache, hookManager *hook.Manager) *CommentService {
|
||||
func NewCommentService(commentRepo repository.CommentRepository, postRepo repository.PostRepository, systemMessageService SystemMessageService, postAIService *PostAIService, cacheBackend cache.Cache, hookManager *hook.Manager) *CommentService {
|
||||
return &CommentService{
|
||||
commentRepo: commentRepo,
|
||||
postRepo: postRepo,
|
||||
|
||||
@@ -106,25 +106,23 @@ type GroupMembersResult struct {
|
||||
|
||||
// groupService 群组服务实现
|
||||
type groupService struct {
|
||||
db *gorm.DB
|
||||
groupRepo repository.GroupRepository
|
||||
userRepo *repository.UserRepository
|
||||
messageRepo *repository.MessageRepository
|
||||
userRepo repository.UserRepository
|
||||
messageRepo repository.MessageRepository
|
||||
requestRepo repository.GroupJoinRequestRepository
|
||||
notifyRepo *repository.SystemNotificationRepository
|
||||
notifyRepo repository.SystemNotificationRepository
|
||||
sseHub *sse.Hub
|
||||
cache cache.Cache
|
||||
}
|
||||
|
||||
// NewGroupService 创建群组服务
|
||||
func NewGroupService(db *gorm.DB, groupRepo repository.GroupRepository, userRepo *repository.UserRepository, messageRepo *repository.MessageRepository, sseHub *sse.Hub, cacheBackend cache.Cache) GroupService {
|
||||
func NewGroupService(groupRepo repository.GroupRepository, userRepo repository.UserRepository, messageRepo repository.MessageRepository, requestRepo repository.GroupJoinRequestRepository, notifyRepo repository.SystemNotificationRepository, sseHub *sse.Hub, cacheBackend cache.Cache) GroupService {
|
||||
return &groupService{
|
||||
db: db,
|
||||
groupRepo: groupRepo,
|
||||
userRepo: userRepo,
|
||||
messageRepo: messageRepo,
|
||||
requestRepo: repository.NewGroupJoinRequestRepository(db),
|
||||
notifyRepo: repository.NewSystemNotificationRepository(db),
|
||||
requestRepo: requestRepo,
|
||||
notifyRepo: notifyRepo,
|
||||
sseHub: sseHub,
|
||||
cache: cacheBackend,
|
||||
}
|
||||
@@ -263,43 +261,17 @@ func (s *groupService) CreateGroup(ownerID string, name string, description stri
|
||||
GroupID: &group.ID,
|
||||
}
|
||||
|
||||
// 在事务中创建会话和参与者
|
||||
err = s.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 创建会话
|
||||
if err := tx.Create(conversation).Error; err != nil {
|
||||
return err
|
||||
// 收集所有参与者
|
||||
participants := make([]string, 0, len(memberIDs)+1)
|
||||
participants = append(participants, ownerID)
|
||||
for _, memberID := range memberIDs {
|
||||
if memberID != ownerID {
|
||||
participants = append(participants, memberID)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加群主为会话参与者
|
||||
ownerParticipant := model.ConversationParticipant{
|
||||
ConversationID: conversation.ID,
|
||||
UserID: ownerID,
|
||||
LastReadSeq: 0,
|
||||
}
|
||||
if err := tx.Create(&ownerParticipant).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 添加被邀请的成员为会话参与者
|
||||
for _, memberID := range memberIDs {
|
||||
if memberID == ownerID {
|
||||
continue
|
||||
}
|
||||
participant := model.ConversationParticipant{
|
||||
ConversationID: conversation.ID,
|
||||
UserID: memberID,
|
||||
LastReadSeq: 0,
|
||||
}
|
||||
if err := tx.Create(&participant).Error; err != nil {
|
||||
// 单个参与者添加失败继续处理其他成员
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// 使用仓储创建会话和参与者
|
||||
if err := s.messageRepo.CreateConversationWithParticipants(conversation, participants); err != nil {
|
||||
// 记录错误但不影响群组创建成功
|
||||
}
|
||||
|
||||
@@ -418,30 +390,8 @@ func (s *groupService) TransferOwner(userID string, groupID string, newOwnerID s
|
||||
return ErrNotGroupMember
|
||||
}
|
||||
|
||||
// 在事务中更新
|
||||
return s.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 更新群组的群主
|
||||
group.OwnerID = newOwnerID
|
||||
if err := tx.Save(group).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新原群主为管理员
|
||||
if err := tx.Model(&model.GroupMember{}).
|
||||
Where("group_id = ? AND user_id = ?", groupID, userID).
|
||||
Update("role", model.GroupRoleAdmin).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新新群主为群主
|
||||
if err := tx.Model(&model.GroupMember{}).
|
||||
Where("group_id = ? AND user_id = ?", groupID, newOwnerID).
|
||||
Update("role", model.GroupRoleOwner).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
// 使用仓储的 TransferOwner 方法(在事务中更新)
|
||||
return s.groupRepo.TransferOwner(groupID, userID, newOwnerID)
|
||||
}
|
||||
|
||||
// GetUserGroups 获取用户加入的群组列表
|
||||
@@ -593,9 +543,7 @@ func (s *groupService) collectGroupReviewerIDs(groupID string, ownerID string, s
|
||||
if ownerID != "" && ownerID != skipUserID {
|
||||
reviewerSet[ownerID] = struct{}{}
|
||||
}
|
||||
|
||||
var reviewers []model.GroupMember
|
||||
if err := s.db.Where("group_id = ? AND role IN ?", groupID, []string{model.GroupRoleOwner, model.GroupRoleAdmin}).Find(&reviewers).Error; err == nil {
|
||||
if reviewers, err := s.groupRepo.GetAdmins(groupID); err == nil {
|
||||
for _, reviewer := range reviewers {
|
||||
if reviewer.UserID == "" || reviewer.UserID == skipUserID {
|
||||
continue
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
// HotRankWorker 定时在候选池上重算热门分,写入 Redis ZSET(仅 TopN)及 top_ids(Redis+本地缓存)
|
||||
type HotRankWorker struct {
|
||||
cfg *config.Config
|
||||
postRepo *repository.PostRepository
|
||||
postRepo repository.PostRepository
|
||||
c cache.Cache
|
||||
mu sync.Mutex
|
||||
stopOnce sync.Once
|
||||
@@ -28,7 +28,7 @@ type HotRankWorker struct {
|
||||
}
|
||||
|
||||
// NewHotRankWorker 创建热门榜重算 worker(cache 需为 Redis 实现才有意义)
|
||||
func NewHotRankWorker(cfg *config.Config, postRepo *repository.PostRepository, c cache.Cache) *HotRankWorker {
|
||||
func NewHotRankWorker(cfg *config.Config, postRepo repository.PostRepository, c cache.Cache) *HotRankWorker {
|
||||
return &HotRankWorker{
|
||||
cfg: cfg,
|
||||
postRepo: postRepo,
|
||||
|
||||
@@ -30,9 +30,9 @@ type LogCleanupService interface {
|
||||
|
||||
// logCleanupServiceImpl 日志清理服务实现
|
||||
type logCleanupServiceImpl struct {
|
||||
operationRepo *repository.OperationLogRepository
|
||||
loginRepo *repository.LoginLogRepository
|
||||
dataChangeRepo *repository.DataChangeLogRepository
|
||||
operationRepo repository.OperationLogRepository
|
||||
loginRepo repository.LoginLogRepository
|
||||
dataChangeRepo repository.DataChangeLogRepository
|
||||
cfg *LogCleanupConfig
|
||||
logger *zap.Logger
|
||||
ctx context.Context
|
||||
@@ -41,9 +41,9 @@ type logCleanupServiceImpl struct {
|
||||
|
||||
// NewLogCleanupService 创建日志清理服务
|
||||
func NewLogCleanupService(
|
||||
operationRepo *repository.OperationLogRepository,
|
||||
loginRepo *repository.LoginLogRepository,
|
||||
dataChangeRepo *repository.DataChangeLogRepository,
|
||||
operationRepo repository.OperationLogRepository,
|
||||
loginRepo repository.LoginLogRepository,
|
||||
dataChangeRepo repository.DataChangeLogRepository,
|
||||
cfg *LogCleanupConfig,
|
||||
logger *zap.Logger,
|
||||
) LogCleanupService {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/model"
|
||||
"carrot_bbs/internal/repository"
|
||||
)
|
||||
@@ -11,7 +12,7 @@ import (
|
||||
// OperationLogService 操作日志服务接口
|
||||
type OperationLogService interface {
|
||||
RecordOperation(log *model.OperationLog)
|
||||
GetOperationLogs(ctx context.Context, filters repository.LogFilter, page, pageSize int) ([]*model.OperationLog, int64, error)
|
||||
GetOperationLogs(ctx context.Context, filters dto.LogFilter, page, pageSize int) ([]*model.OperationLog, int64, error)
|
||||
GetOperationLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.OperationLog, int64, error)
|
||||
GetOperationLogsByTimeRange(ctx context.Context, startTime, endTime string, page, pageSize int) ([]*model.OperationLog, int64, error)
|
||||
GetStatistics(ctx context.Context, startTime, endTime string) (*repository.OperationLogStatistics, error)
|
||||
@@ -20,13 +21,13 @@ type OperationLogService interface {
|
||||
// operationLogServiceImpl 操作日志服务实现
|
||||
type operationLogServiceImpl struct {
|
||||
asyncManager *AsyncLogManager
|
||||
repo *repository.OperationLogRepository
|
||||
repo repository.OperationLogRepository
|
||||
}
|
||||
|
||||
// NewOperationLogService 创建操作日志服务
|
||||
func NewOperationLogService(
|
||||
asyncManager *AsyncLogManager,
|
||||
repo *repository.OperationLogRepository,
|
||||
repo repository.OperationLogRepository,
|
||||
) OperationLogService {
|
||||
return &operationLogServiceImpl{
|
||||
asyncManager: asyncManager,
|
||||
@@ -40,7 +41,7 @@ func (s *operationLogServiceImpl) RecordOperation(log *model.OperationLog) {
|
||||
}
|
||||
|
||||
// GetOperationLogs 获取操作日志列表
|
||||
func (s *operationLogServiceImpl) GetOperationLogs(ctx context.Context, filters repository.LogFilter, page, pageSize int) ([]*model.OperationLog, int64, error) {
|
||||
func (s *operationLogServiceImpl) GetOperationLogs(ctx context.Context, filters dto.LogFilter, page, pageSize int) ([]*model.OperationLog, int64, error) {
|
||||
return s.repo.GetOperationLogs(ctx, filters, page, pageSize)
|
||||
}
|
||||
|
||||
@@ -62,7 +63,7 @@ func (s *operationLogServiceImpl) GetStatistics(ctx context.Context, startTime,
|
||||
// LoginLogService 登录日志服务接口
|
||||
type LoginLogService interface {
|
||||
RecordLogin(log *model.LoginLog)
|
||||
GetLoginLogs(ctx context.Context, filters repository.LoginFilter, page, pageSize int) ([]*model.LoginLog, int64, error)
|
||||
GetLoginLogs(ctx context.Context, filters dto.LoginFilter, page, pageSize int) ([]*model.LoginLog, int64, error)
|
||||
GetLoginLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.LoginLog, int64, error)
|
||||
GetFailedLoginsByIP(ctx context.Context, ip string, timeWindow string) (int64, error)
|
||||
GetRecentSuccessLogins(ctx context.Context, userID string, limit int) ([]*model.LoginLog, error)
|
||||
@@ -72,13 +73,13 @@ type LoginLogService interface {
|
||||
// loginLogServiceImpl 登录日志服务实现
|
||||
type loginLogServiceImpl struct {
|
||||
asyncManager *AsyncLogManager
|
||||
repo *repository.LoginLogRepository
|
||||
repo repository.LoginLogRepository
|
||||
}
|
||||
|
||||
// NewLoginLogService 创建登录日志服务
|
||||
func NewLoginLogService(
|
||||
asyncManager *AsyncLogManager,
|
||||
repo *repository.LoginLogRepository,
|
||||
repo repository.LoginLogRepository,
|
||||
) LoginLogService {
|
||||
return &loginLogServiceImpl{
|
||||
asyncManager: asyncManager,
|
||||
@@ -92,7 +93,7 @@ func (s *loginLogServiceImpl) RecordLogin(log *model.LoginLog) {
|
||||
}
|
||||
|
||||
// GetLoginLogs 获取登录日志列表
|
||||
func (s *loginLogServiceImpl) GetLoginLogs(ctx context.Context, filters repository.LoginFilter, page, pageSize int) ([]*model.LoginLog, int64, error) {
|
||||
func (s *loginLogServiceImpl) GetLoginLogs(ctx context.Context, filters dto.LoginFilter, page, pageSize int) ([]*model.LoginLog, int64, error) {
|
||||
return s.repo.GetLoginLogs(ctx, filters, page, pageSize)
|
||||
}
|
||||
|
||||
@@ -123,7 +124,7 @@ func (s *loginLogServiceImpl) GetStatistics(ctx context.Context, startTime, endT
|
||||
// DataChangeLogService 数据变更日志服务接口
|
||||
type DataChangeLogService interface {
|
||||
RecordDataChange(log *model.DataChangeLog)
|
||||
GetDataChangeLogs(ctx context.Context, filters repository.DataChangeFilter, page, pageSize int) ([]*model.DataChangeLog, int64, error)
|
||||
GetDataChangeLogs(ctx context.Context, filters dto.DataChangeFilter, page, pageSize int) ([]*model.DataChangeLog, int64, error)
|
||||
GetDataChangeLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.DataChangeLog, int64, error)
|
||||
GetDataChangeLogsByOperator(ctx context.Context, operatorID string, page, pageSize int) ([]*model.DataChangeLog, int64, error)
|
||||
GetStatistics(ctx context.Context, startTime, endTime string) (*repository.DataChangeLogStatistics, error)
|
||||
@@ -132,13 +133,13 @@ type DataChangeLogService interface {
|
||||
// dataChangeLogServiceImpl 数据变更日志服务实现
|
||||
type dataChangeLogServiceImpl struct {
|
||||
asyncManager *AsyncLogManager
|
||||
repo *repository.DataChangeLogRepository
|
||||
repo repository.DataChangeLogRepository
|
||||
}
|
||||
|
||||
// NewDataChangeLogService 创建数据变更日志服务
|
||||
func NewDataChangeLogService(
|
||||
asyncManager *AsyncLogManager,
|
||||
repo *repository.DataChangeLogRepository,
|
||||
repo repository.DataChangeLogRepository,
|
||||
) DataChangeLogService {
|
||||
return &dataChangeLogServiceImpl{
|
||||
asyncManager: asyncManager,
|
||||
@@ -152,7 +153,7 @@ func (s *dataChangeLogServiceImpl) RecordDataChange(log *model.DataChangeLog) {
|
||||
}
|
||||
|
||||
// GetDataChangeLogs 获取数据变更日志列表
|
||||
func (s *dataChangeLogServiceImpl) GetDataChangeLogs(ctx context.Context, filters repository.DataChangeFilter, page, pageSize int) ([]*model.DataChangeLog, int64, error) {
|
||||
func (s *dataChangeLogServiceImpl) GetDataChangeLogs(ctx context.Context, filters dto.DataChangeFilter, page, pageSize int) ([]*model.DataChangeLog, int64, error) {
|
||||
return s.repo.GetDataChangeLogs(ctx, filters, page, pageSize)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/model"
|
||||
"carrot_bbs/internal/repository"
|
||||
)
|
||||
@@ -21,7 +23,7 @@ type MaterialService interface {
|
||||
DeleteSubject(id string) error
|
||||
|
||||
// 资料相关
|
||||
ListMaterials(params repository.MaterialFileQueryParams) ([]*model.MaterialFile, int64, error)
|
||||
ListMaterials(params dto.MaterialFileQueryParams) ([]*model.MaterialFile, int64, error)
|
||||
GetMaterialByID(id string) (*model.MaterialFile, error)
|
||||
GetMaterialByIDWithSubject(id string) (*model.MaterialFile, error)
|
||||
GetMaterialsBySubject(subjectID string, page, pageSize int) ([]*model.MaterialFile, int64, error)
|
||||
@@ -36,14 +38,14 @@ type MaterialService interface {
|
||||
|
||||
// materialServiceImpl 学习资料服务实现
|
||||
type materialServiceImpl struct {
|
||||
subjectRepo *repository.MaterialSubjectRepository
|
||||
fileRepo *repository.MaterialFileRepository
|
||||
subjectRepo repository.MaterialSubjectRepository
|
||||
fileRepo repository.MaterialFileRepository
|
||||
}
|
||||
|
||||
// NewMaterialService 创建学习资料服务
|
||||
func NewMaterialService(
|
||||
subjectRepo *repository.MaterialSubjectRepository,
|
||||
fileRepo *repository.MaterialFileRepository,
|
||||
subjectRepo repository.MaterialSubjectRepository,
|
||||
fileRepo repository.MaterialFileRepository,
|
||||
) MaterialService {
|
||||
return &materialServiceImpl{
|
||||
subjectRepo: subjectRepo,
|
||||
@@ -127,7 +129,7 @@ func (s *materialServiceImpl) DeleteSubject(id string) error {
|
||||
// 资料相关方法
|
||||
// =====================================================
|
||||
|
||||
func (s *materialServiceImpl) ListMaterials(params repository.MaterialFileQueryParams) ([]*model.MaterialFile, int64, error) {
|
||||
func (s *materialServiceImpl) ListMaterials(params dto.MaterialFileQueryParams) ([]*model.MaterialFile, int64, error) {
|
||||
return s.fileRepo.List(params)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"carrot_bbs/internal/repository"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 缓存TTL常量
|
||||
@@ -25,10 +24,8 @@ const (
|
||||
|
||||
// MessageService 消息服务
|
||||
type MessageService struct {
|
||||
db *gorm.DB
|
||||
|
||||
// 基础仓储
|
||||
messageRepo *repository.MessageRepository
|
||||
messageRepo repository.MessageRepository
|
||||
|
||||
// 缓存相关字段
|
||||
conversationCache *cache.ConversationCache
|
||||
@@ -40,7 +37,7 @@ type MessageService struct {
|
||||
}
|
||||
|
||||
// NewMessageService 创建消息服务
|
||||
func NewMessageService(db *gorm.DB, messageRepo *repository.MessageRepository, cacheBackend cache.Cache, uploadService *UploadService) *MessageService {
|
||||
func NewMessageService(messageRepo repository.MessageRepository, cacheBackend cache.Cache, uploadService *UploadService) *MessageService {
|
||||
// 创建适配器
|
||||
convRepoAdapter := cache.NewConversationRepositoryAdapter(messageRepo)
|
||||
msgRepoAdapter := cache.NewMessageRepositoryAdapter(messageRepo)
|
||||
@@ -54,7 +51,6 @@ func NewMessageService(db *gorm.DB, messageRepo *repository.MessageRepository, c
|
||||
)
|
||||
|
||||
return &MessageService{
|
||||
db: db,
|
||||
messageRepo: messageRepo,
|
||||
conversationCache: conversationCache,
|
||||
baseCache: cacheBackend,
|
||||
@@ -161,7 +157,7 @@ func (s *MessageService) GetConversations(ctx context.Context, userID string, pa
|
||||
|
||||
// 生成缓存键
|
||||
cacheKey := cache.ConversationListKey(userID, page, pageSize)
|
||||
result, err := cache.GetOrLoadTyped[*ConversationListResult](
|
||||
result, err := cache.GetOrLoadTyped(
|
||||
s.baseCache,
|
||||
cacheKey,
|
||||
conversationTTL,
|
||||
@@ -252,7 +248,7 @@ func (s *MessageService) GetUnreadCount(ctx context.Context, conversationID stri
|
||||
// 生成缓存键
|
||||
cacheKey := cache.UnreadDetailKey(userID, conversationID)
|
||||
|
||||
return cache.GetOrLoadTyped[int64](
|
||||
return cache.GetOrLoadTyped(
|
||||
s.baseCache,
|
||||
cacheKey,
|
||||
unreadTTL,
|
||||
|
||||
@@ -20,12 +20,12 @@ const (
|
||||
|
||||
// NotificationService 通知服务
|
||||
type NotificationService struct {
|
||||
notificationRepo *repository.NotificationRepository
|
||||
notificationRepo repository.NotificationRepository
|
||||
cache cache.Cache
|
||||
}
|
||||
|
||||
// NewNotificationService 创建通知服务
|
||||
func NewNotificationService(notificationRepo *repository.NotificationRepository, cacheBackend cache.Cache) *NotificationService {
|
||||
func NewNotificationService(notificationRepo repository.NotificationRepository, cacheBackend cache.Cache) *NotificationService {
|
||||
return &NotificationService{
|
||||
notificationRepo: notificationRepo,
|
||||
cache: cacheBackend,
|
||||
|
||||
@@ -73,7 +73,7 @@ type PostService interface {
|
||||
|
||||
// postServiceImpl 帖子服务实现
|
||||
type postServiceImpl struct {
|
||||
postRepo *repository.PostRepository
|
||||
postRepo repository.PostRepository
|
||||
systemMessageService SystemMessageService
|
||||
cache cache.Cache
|
||||
postAIService *PostAIService
|
||||
@@ -82,7 +82,7 @@ type postServiceImpl struct {
|
||||
hookManager *hook.Manager
|
||||
}
|
||||
|
||||
func NewPostService(postRepo *repository.PostRepository, systemMessageService SystemMessageService, postAIService *PostAIService, cacheBackend cache.Cache, txManager repository.TransactionManager, hookManager *hook.Manager) PostService {
|
||||
func NewPostService(postRepo repository.PostRepository, systemMessageService SystemMessageService, postAIService *PostAIService, cacheBackend cache.Cache, txManager repository.TransactionManager, hookManager *hook.Manager) PostService {
|
||||
return &postServiceImpl{
|
||||
postRepo: postRepo,
|
||||
systemMessageService: systemMessageService,
|
||||
|
||||
@@ -62,9 +62,9 @@ type PushService interface {
|
||||
|
||||
// pushServiceImpl 推送服务实现
|
||||
type pushServiceImpl struct {
|
||||
pushRepo *repository.PushRecordRepository
|
||||
deviceRepo *repository.DeviceTokenRepository
|
||||
messageRepo *repository.MessageRepository
|
||||
pushRepo repository.PushRecordRepository
|
||||
deviceRepo repository.DeviceTokenRepository
|
||||
messageRepo repository.MessageRepository
|
||||
sseHub *sse.Hub
|
||||
|
||||
// 推送队列
|
||||
@@ -81,9 +81,9 @@ type pushTask struct {
|
||||
|
||||
// NewPushService 创建推送服务
|
||||
func NewPushService(
|
||||
pushRepo *repository.PushRecordRepository,
|
||||
deviceRepo *repository.DeviceTokenRepository,
|
||||
messageRepo *repository.MessageRepository,
|
||||
pushRepo repository.PushRecordRepository,
|
||||
deviceRepo repository.DeviceTokenRepository,
|
||||
messageRepo repository.MessageRepository,
|
||||
sseHub *sse.Hub,
|
||||
) PushService {
|
||||
return &pushServiceImpl{
|
||||
|
||||
@@ -26,22 +26,30 @@ type SystemMessageService interface {
|
||||
// 发送系统公告
|
||||
SendSystemAnnouncement(ctx context.Context, userIDs []string, title string, content string) error
|
||||
SendBroadcastAnnouncement(ctx context.Context, title string, content string) error
|
||||
|
||||
// 查询通知(供 Handler 层的 REST API 使用)
|
||||
GetNotifications(receiverID string, page, pageSize int) ([]*model.SystemNotification, int64, error)
|
||||
GetNotificationsByCursor(receiverID string, cursorStr string, pageSize int) ([]*model.SystemNotification, string, bool, error)
|
||||
GetUnreadCount(receiverID string) (int64, error)
|
||||
MarkAsRead(notificationID int64, receiverID string) error
|
||||
MarkAllAsRead(receiverID string) error
|
||||
DeleteNotification(notificationID int64, receiverID string) error
|
||||
}
|
||||
|
||||
type systemMessageServiceImpl struct {
|
||||
notifyRepo *repository.SystemNotificationRepository
|
||||
notifyRepo repository.SystemNotificationRepository
|
||||
pushService PushService
|
||||
userRepo *repository.UserRepository
|
||||
postRepo *repository.PostRepository
|
||||
userRepo repository.UserRepository
|
||||
postRepo repository.PostRepository
|
||||
cache cache.Cache
|
||||
}
|
||||
|
||||
// NewSystemMessageService 创建系统消息服务
|
||||
func NewSystemMessageService(
|
||||
notifyRepo *repository.SystemNotificationRepository,
|
||||
notifyRepo repository.SystemNotificationRepository,
|
||||
pushService PushService,
|
||||
userRepo *repository.UserRepository,
|
||||
postRepo *repository.PostRepository,
|
||||
userRepo repository.UserRepository,
|
||||
postRepo repository.PostRepository,
|
||||
cacheBackend cache.Cache,
|
||||
) SystemMessageService {
|
||||
return &systemMessageServiceImpl{
|
||||
@@ -402,6 +410,51 @@ func (s *systemMessageServiceImpl) createNotification(ctx context.Context, userI
|
||||
return notification, nil
|
||||
}
|
||||
|
||||
// GetNotifications 获取通知列表(供 Handler 调用)
|
||||
func (s *systemMessageServiceImpl) GetNotifications(receiverID string, page, pageSize int) ([]*model.SystemNotification, int64, error) {
|
||||
return s.notifyRepo.GetByReceiverID(receiverID, page, pageSize)
|
||||
}
|
||||
|
||||
// GetNotificationsByCursor 游标分页获取通知列表(供 Handler 调用)
|
||||
func (s *systemMessageServiceImpl) GetNotificationsByCursor(receiverID string, cursorStr string, pageSize int) ([]*model.SystemNotification, string, bool, error) {
|
||||
return s.notifyRepo.GetByReceiverIDCursorLegacy(receiverID, cursorStr, pageSize)
|
||||
}
|
||||
|
||||
// GetUnreadCount 获取未读数量(供 Handler 调用)
|
||||
func (s *systemMessageServiceImpl) GetUnreadCount(receiverID string) (int64, error) {
|
||||
return s.notifyRepo.GetUnreadCount(receiverID)
|
||||
}
|
||||
|
||||
// MarkAsRead 标记单条为已读(供 Handler 调用)
|
||||
func (s *systemMessageServiceImpl) MarkAsRead(notificationID int64, receiverID string) error {
|
||||
err := s.notifyRepo.MarkAsRead(notificationID, receiverID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache.InvalidateUnreadSystem(s.cache, receiverID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkAllAsRead 标记全部为已读(供 Handler 调用)
|
||||
func (s *systemMessageServiceImpl) MarkAllAsRead(receiverID string) error {
|
||||
err := s.notifyRepo.MarkAllAsRead(receiverID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache.InvalidateUnreadSystem(s.cache, receiverID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteNotification 删除通知(供 Handler 调用)
|
||||
func (s *systemMessageServiceImpl) DeleteNotification(notificationID int64, receiverID string) error {
|
||||
err := s.notifyRepo.Delete(notificationID, receiverID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache.InvalidateUnreadSystem(s.cache, receiverID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// getActorInfo 获取操作者信息
|
||||
func (s *systemMessageServiceImpl) getActorInfo(ctx context.Context, operatorID string) (string, string, error) {
|
||||
// 从用户仓储获取用户信息
|
||||
|
||||
@@ -35,11 +35,11 @@ type UserActivityService interface {
|
||||
|
||||
// userActivityService 实现
|
||||
type userActivityService struct {
|
||||
repo *repository.UserActivityRepository
|
||||
repo repository.UserActivityRepository
|
||||
}
|
||||
|
||||
// NewUserActivityService 创建用户活跃统计服务
|
||||
func NewUserActivityService(repo *repository.UserActivityRepository) UserActivityService {
|
||||
func NewUserActivityService(repo repository.UserActivityRepository) UserActivityService {
|
||||
return &userActivityService{repo: repo}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ type UserService interface {
|
||||
|
||||
// userServiceImpl 用户服务实现
|
||||
type userServiceImpl struct {
|
||||
userRepo *repository.UserRepository
|
||||
userRepo repository.UserRepository
|
||||
systemMessageService SystemMessageService
|
||||
emailCodeService EmailCodeService
|
||||
logService *LogService
|
||||
@@ -67,7 +67,7 @@ type userServiceImpl struct {
|
||||
|
||||
// NewUserService 创建用户服务
|
||||
func NewUserService(
|
||||
userRepo *repository.UserRepository,
|
||||
userRepo repository.UserRepository,
|
||||
systemMessageService SystemMessageService,
|
||||
emailService EmailService,
|
||||
cacheBackend cache.Cache,
|
||||
|
||||
@@ -16,8 +16,8 @@ import (
|
||||
|
||||
// VoteService 投票服务
|
||||
type VoteService struct {
|
||||
voteRepo *repository.VoteRepository
|
||||
postRepo *repository.PostRepository
|
||||
voteRepo repository.VoteRepository
|
||||
postRepo repository.PostRepository
|
||||
cache cache.Cache
|
||||
postAIService *PostAIService
|
||||
systemMessageService SystemMessageService
|
||||
@@ -25,8 +25,8 @@ type VoteService struct {
|
||||
|
||||
// NewVoteService 创建投票服务
|
||||
func NewVoteService(
|
||||
voteRepo *repository.VoteRepository,
|
||||
postRepo *repository.PostRepository,
|
||||
voteRepo repository.VoteRepository,
|
||||
postRepo repository.PostRepository,
|
||||
cache cache.Cache,
|
||||
postAIService *PostAIService,
|
||||
systemMessageService SystemMessageService,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package wire
|
||||
|
||||
import (
|
||||
"carrot_bbs/internal/cache"
|
||||
"carrot_bbs/internal/handler"
|
||||
"carrot_bbs/internal/pkg/sse"
|
||||
"carrot_bbs/internal/repository"
|
||||
"carrot_bbs/internal/service"
|
||||
|
||||
"github.com/google/wire"
|
||||
@@ -101,10 +99,8 @@ func ProvideMessageHandler(
|
||||
// ProvideSystemMessageHandler 提供系统消息处理器
|
||||
func ProvideSystemMessageHandler(
|
||||
systemMsgService service.SystemMessageService,
|
||||
systemNotificationRepo *repository.SystemNotificationRepository,
|
||||
cacheBackend cache.Cache,
|
||||
) *handler.SystemMessageHandler {
|
||||
return handler.NewSystemMessageHandler(systemMsgService, systemNotificationRepo, cacheBackend)
|
||||
return handler.NewSystemMessageHandler(systemMsgService)
|
||||
}
|
||||
|
||||
// ProvideGroupHandler 提供群组处理器
|
||||
|
||||
@@ -135,8 +135,8 @@ func ProvideBuiltinHooks(manager *hook.Manager) *hook.BuiltinHooks {
|
||||
func ProvideModerationHooks(
|
||||
manager *hook.Manager,
|
||||
postAIService *service.PostAIService,
|
||||
postRepo *repository.PostRepository,
|
||||
commentRepo *repository.CommentRepository,
|
||||
postRepo repository.PostRepository,
|
||||
commentRepo repository.CommentRepository,
|
||||
cfg *config.Config,
|
||||
) *hook.ModerationHooks {
|
||||
strictMode := cfg.OpenAI.StrictModeration
|
||||
|
||||
@@ -19,6 +19,7 @@ var RepositorySet = wire.NewSet(
|
||||
repository.NewDeviceTokenRepository,
|
||||
repository.NewSystemNotificationRepository,
|
||||
repository.NewGroupRepository,
|
||||
repository.NewGroupJoinRequestRepository,
|
||||
repository.NewStickerRepository,
|
||||
repository.NewVoteRepository,
|
||||
repository.NewChannelRepository,
|
||||
@@ -34,7 +35,7 @@ var RepositorySet = wire.NewSet(
|
||||
)
|
||||
|
||||
// ProvideUserActivityRepository 提供用户活跃数据仓储
|
||||
func ProvideUserActivityRepository(db *gorm.DB, cache cache.Cache) *repository.UserActivityRepository {
|
||||
func ProvideUserActivityRepository(db *gorm.DB, cache cache.Cache) repository.UserActivityRepository {
|
||||
return repository.NewUserActivityRepository(db, cache)
|
||||
}
|
||||
|
||||
|
||||
@@ -86,9 +86,9 @@ func ProvidePostAIService(client openai.Client) *service.PostAIService {
|
||||
|
||||
// ProvidePushService 提供推送服务
|
||||
func ProvidePushService(
|
||||
pushRepo *repository.PushRecordRepository,
|
||||
deviceTokenRepo *repository.DeviceTokenRepository,
|
||||
messageRepo *repository.MessageRepository,
|
||||
pushRepo repository.PushRecordRepository,
|
||||
deviceTokenRepo repository.DeviceTokenRepository,
|
||||
messageRepo repository.MessageRepository,
|
||||
sseHub *sse.Hub,
|
||||
) service.PushService {
|
||||
return service.NewPushService(pushRepo, deviceTokenRepo, messageRepo, sseHub)
|
||||
@@ -96,17 +96,17 @@ func ProvidePushService(
|
||||
|
||||
// ProvideSystemMessageService 提供系统消息服务
|
||||
func ProvideSystemMessageService(
|
||||
notifyRepo *repository.SystemNotificationRepository,
|
||||
notifyRepo repository.SystemNotificationRepository,
|
||||
pushService service.PushService,
|
||||
userRepo *repository.UserRepository,
|
||||
postRepo *repository.PostRepository,
|
||||
userRepo repository.UserRepository,
|
||||
postRepo repository.PostRepository,
|
||||
cacheBackend cache.Cache,
|
||||
) service.SystemMessageService {
|
||||
return service.NewSystemMessageService(notifyRepo, pushService, userRepo, postRepo, cacheBackend)
|
||||
}
|
||||
|
||||
func ProvidePostService(
|
||||
postRepo *repository.PostRepository,
|
||||
postRepo repository.PostRepository,
|
||||
systemMessageService service.SystemMessageService,
|
||||
postAIService *service.PostAIService,
|
||||
cacheBackend cache.Cache,
|
||||
@@ -119,8 +119,8 @@ func ProvidePostService(
|
||||
}
|
||||
|
||||
func ProvideCommentService(
|
||||
commentRepo *repository.CommentRepository,
|
||||
postRepo *repository.PostRepository,
|
||||
commentRepo repository.CommentRepository,
|
||||
postRepo repository.PostRepository,
|
||||
systemMessageService service.SystemMessageService,
|
||||
postAIService *service.PostAIService,
|
||||
cacheBackend cache.Cache,
|
||||
@@ -131,17 +131,16 @@ func ProvideCommentService(
|
||||
|
||||
// ProvideMessageService 提供消息服务
|
||||
func ProvideMessageService(
|
||||
db *gorm.DB,
|
||||
messageRepo *repository.MessageRepository,
|
||||
messageRepo repository.MessageRepository,
|
||||
cacheBackend cache.Cache,
|
||||
uploadService *service.UploadService,
|
||||
) *service.MessageService {
|
||||
return service.NewMessageService(db, messageRepo, cacheBackend, uploadService)
|
||||
return service.NewMessageService(messageRepo, cacheBackend, uploadService)
|
||||
}
|
||||
|
||||
// ProvideNotificationService 提供通知服务
|
||||
func ProvideNotificationService(
|
||||
notificationRepo *repository.NotificationRepository,
|
||||
notificationRepo repository.NotificationRepository,
|
||||
cacheBackend cache.Cache,
|
||||
) *service.NotificationService {
|
||||
return service.NewNotificationService(notificationRepo, cacheBackend)
|
||||
@@ -149,7 +148,7 @@ func ProvideNotificationService(
|
||||
|
||||
// ProvideUserService 提供用户服务
|
||||
func ProvideUserService(
|
||||
userRepo *repository.UserRepository,
|
||||
userRepo repository.UserRepository,
|
||||
systemMessageService service.SystemMessageService,
|
||||
emailService service.EmailService,
|
||||
cacheBackend cache.Cache,
|
||||
@@ -166,8 +165,8 @@ func ProvideStickerService(
|
||||
|
||||
// ProvideVoteService 提供投票服务
|
||||
func ProvideVoteService(
|
||||
voteRepo *repository.VoteRepository,
|
||||
postRepo *repository.PostRepository,
|
||||
voteRepo repository.VoteRepository,
|
||||
postRepo repository.PostRepository,
|
||||
cache cache.Cache,
|
||||
postAIService *service.PostAIService,
|
||||
systemMessageService service.SystemMessageService,
|
||||
@@ -175,21 +174,20 @@ func ProvideVoteService(
|
||||
return service.NewVoteService(voteRepo, postRepo, cache, postAIService, systemMessageService)
|
||||
}
|
||||
|
||||
func ProvideChannelService(channelRepo *repository.ChannelRepository, cacheBackend cache.Cache) service.ChannelService {
|
||||
func ProvideChannelService(channelRepo repository.ChannelRepository, cacheBackend cache.Cache) service.ChannelService {
|
||||
return service.NewChannelService(channelRepo, cacheBackend)
|
||||
}
|
||||
|
||||
// ProvideChatService 提供聊天服务
|
||||
// Note: sensitiveService 传 nil,与 main.go 保持一致
|
||||
func ProvideChatService(
|
||||
db *gorm.DB,
|
||||
messageRepo *repository.MessageRepository,
|
||||
userRepo *repository.UserRepository,
|
||||
messageRepo repository.MessageRepository,
|
||||
userRepo repository.UserRepository,
|
||||
sseHub *sse.Hub,
|
||||
cacheBackend cache.Cache,
|
||||
uploadService *service.UploadService,
|
||||
) service.ChatService {
|
||||
return service.NewChatService(db, messageRepo, userRepo, nil, sseHub, cacheBackend, uploadService)
|
||||
return service.NewChatService(messageRepo, userRepo, nil, sseHub, cacheBackend, uploadService)
|
||||
}
|
||||
|
||||
// ProvideScheduleService 提供日程服务
|
||||
@@ -210,14 +208,15 @@ func ProvideScheduleSyncService(
|
||||
|
||||
// ProvideGroupService 提供群组服务
|
||||
func ProvideGroupService(
|
||||
db *gorm.DB,
|
||||
groupRepo repository.GroupRepository,
|
||||
userRepo *repository.UserRepository,
|
||||
messageRepo *repository.MessageRepository,
|
||||
userRepo repository.UserRepository,
|
||||
messageRepo repository.MessageRepository,
|
||||
requestRepo repository.GroupJoinRequestRepository,
|
||||
notifyRepo repository.SystemNotificationRepository,
|
||||
sseHub *sse.Hub,
|
||||
cacheBackend cache.Cache,
|
||||
) service.GroupService {
|
||||
return service.NewGroupService(db, groupRepo, userRepo, messageRepo, sseHub, cacheBackend)
|
||||
return service.NewGroupService(groupRepo, userRepo, messageRepo, requestRepo, notifyRepo, sseHub, cacheBackend)
|
||||
}
|
||||
|
||||
// ProvideUploadService 提供上传服务
|
||||
@@ -229,7 +228,7 @@ func ProvideUploadService(
|
||||
}
|
||||
|
||||
// ProvideUserActivityService 提供用户活跃统计服务
|
||||
func ProvideUserActivityService(repo *repository.UserActivityRepository) service.UserActivityService {
|
||||
func ProvideUserActivityService(repo repository.UserActivityRepository) service.UserActivityService {
|
||||
return service.NewUserActivityService(repo)
|
||||
}
|
||||
|
||||
@@ -242,24 +241,24 @@ func ProvideRoleService(
|
||||
}
|
||||
|
||||
// ProvideAdminUserService 提供管理端用户服务
|
||||
func ProvideAdminUserService(userRepo *repository.UserRepository) service.AdminUserService {
|
||||
func ProvideAdminUserService(userRepo repository.UserRepository) service.AdminUserService {
|
||||
return service.NewAdminUserService(userRepo)
|
||||
}
|
||||
|
||||
// ProvideAdminPostService 提供管理端帖子服务
|
||||
func ProvideAdminPostService(postRepo *repository.PostRepository, cacheBackend cache.Cache) service.AdminPostService {
|
||||
func ProvideAdminPostService(postRepo repository.PostRepository, cacheBackend cache.Cache) service.AdminPostService {
|
||||
return service.NewAdminPostService(postRepo, cacheBackend)
|
||||
}
|
||||
|
||||
// ProvideAdminCommentService 提供管理端评论服务
|
||||
func ProvideAdminCommentService(commentRepo *repository.CommentRepository, postRepo *repository.PostRepository) service.AdminCommentService {
|
||||
func ProvideAdminCommentService(commentRepo repository.CommentRepository, postRepo repository.PostRepository) service.AdminCommentService {
|
||||
return service.NewAdminCommentService(commentRepo, postRepo)
|
||||
}
|
||||
|
||||
// ProvideAdminGroupService 提供管理端群组服务
|
||||
func ProvideAdminGroupService(
|
||||
groupRepo repository.GroupRepository,
|
||||
userRepo *repository.UserRepository,
|
||||
userRepo repository.UserRepository,
|
||||
) service.AdminGroupService {
|
||||
return service.NewAdminGroupService(groupRepo, userRepo)
|
||||
}
|
||||
@@ -267,21 +266,20 @@ func ProvideAdminGroupService(
|
||||
// ProvideAdminDashboardService 提供管理端仪表盘服务
|
||||
func ProvideAdminDashboardService(
|
||||
db *gorm.DB,
|
||||
userRepo *repository.UserRepository,
|
||||
postRepo *repository.PostRepository,
|
||||
commentRepo *repository.CommentRepository,
|
||||
userRepo repository.UserRepository,
|
||||
postRepo repository.PostRepository,
|
||||
commentRepo repository.CommentRepository,
|
||||
groupRepo repository.GroupRepository,
|
||||
activityRepo *repository.UserActivityRepository,
|
||||
activityRepo repository.UserActivityRepository,
|
||||
) service.AdminDashboardService {
|
||||
return service.NewAdminDashboardService(db, userRepo, postRepo, commentRepo, groupRepo, activityRepo)
|
||||
}
|
||||
|
||||
// ProvideAsyncLogManager 提供异步日志管理器
|
||||
func ProvideAsyncLogManager(
|
||||
db *gorm.DB,
|
||||
operationRepo *repository.OperationLogRepository,
|
||||
loginRepo *repository.LoginLogRepository,
|
||||
dataChangeRepo *repository.DataChangeLogRepository,
|
||||
operationRepo repository.OperationLogRepository,
|
||||
loginRepo repository.LoginLogRepository,
|
||||
dataChangeRepo repository.DataChangeLogRepository,
|
||||
logger *zap.Logger,
|
||||
cfg *config.Config,
|
||||
) *service.AsyncLogManager {
|
||||
@@ -300,13 +298,13 @@ func ProvideAsyncLogManager(
|
||||
asyncCfg.FlushInterval = 3 * time.Second
|
||||
}
|
||||
|
||||
return service.NewAsyncLogManager(db, operationRepo, loginRepo, dataChangeRepo, logger, asyncCfg)
|
||||
return service.NewAsyncLogManager(operationRepo, loginRepo, dataChangeRepo, logger, asyncCfg)
|
||||
}
|
||||
|
||||
// ProvideOperationLogService 提供操作日志服务
|
||||
func ProvideOperationLogService(
|
||||
asyncManager *service.AsyncLogManager,
|
||||
operationRepo *repository.OperationLogRepository,
|
||||
operationRepo repository.OperationLogRepository,
|
||||
) service.OperationLogService {
|
||||
return service.NewOperationLogService(asyncManager, operationRepo)
|
||||
}
|
||||
@@ -314,7 +312,7 @@ func ProvideOperationLogService(
|
||||
// ProvideLoginLogService 提供登录日志服务
|
||||
func ProvideLoginLogService(
|
||||
asyncManager *service.AsyncLogManager,
|
||||
loginRepo *repository.LoginLogRepository,
|
||||
loginRepo repository.LoginLogRepository,
|
||||
) service.LoginLogService {
|
||||
return service.NewLoginLogService(asyncManager, loginRepo)
|
||||
}
|
||||
@@ -322,16 +320,16 @@ func ProvideLoginLogService(
|
||||
// ProvideDataChangeLogService 提供数据变更日志服务
|
||||
func ProvideDataChangeLogService(
|
||||
asyncManager *service.AsyncLogManager,
|
||||
dataChangeRepo *repository.DataChangeLogRepository,
|
||||
dataChangeRepo repository.DataChangeLogRepository,
|
||||
) service.DataChangeLogService {
|
||||
return service.NewDataChangeLogService(asyncManager, dataChangeRepo)
|
||||
}
|
||||
|
||||
// ProvideLogCleanupService 提供日志清理服务
|
||||
func ProvideLogCleanupService(
|
||||
operationRepo *repository.OperationLogRepository,
|
||||
loginRepo *repository.LoginLogRepository,
|
||||
dataChangeRepo *repository.DataChangeLogRepository,
|
||||
operationRepo repository.OperationLogRepository,
|
||||
loginRepo repository.LoginLogRepository,
|
||||
dataChangeRepo repository.DataChangeLogRepository,
|
||||
logger *zap.Logger,
|
||||
cfg *config.Config,
|
||||
) service.LogCleanupService {
|
||||
@@ -355,7 +353,7 @@ func ProvideLogService(
|
||||
}
|
||||
|
||||
// ProvideQRCodeLoginService 提供二维码登录服务
|
||||
func ProvideHotRankWorker(cfg *config.Config, postRepo *repository.PostRepository, cacheBackend cache.Cache) *service.HotRankWorker {
|
||||
func ProvideHotRankWorker(cfg *config.Config, postRepo repository.PostRepository, cacheBackend cache.Cache) *service.HotRankWorker {
|
||||
return service.NewHotRankWorker(cfg, postRepo, cacheBackend)
|
||||
}
|
||||
|
||||
@@ -372,8 +370,8 @@ func ProvideQRCodeLoginService(
|
||||
|
||||
// ProvideMaterialService 提供学习资料服务
|
||||
func ProvideMaterialService(
|
||||
subjectRepo *repository.MaterialSubjectRepository,
|
||||
fileRepo *repository.MaterialFileRepository,
|
||||
subjectRepo repository.MaterialSubjectRepository,
|
||||
fileRepo repository.MaterialFileRepository,
|
||||
) service.MaterialService {
|
||||
return service.NewMaterialService(subjectRepo, fileRepo)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user