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