refactor: introduce Wire dependency injection and interface-based architecture

- Add Google Wire for compile-time dependency injection
- Replace concrete service types with interfaces across handlers
- Remove global state (DB, cache) in favor of constructor injection
- Split monolithic files into focused modules:
  - config: separate files for each config domain
  - dto: converters split by domain (user, post, message, group)
  - cache: separate metrics.go and redis_cache.go
- Introduce unified apperrors package with string-based error codes
- Add transaction support with context-aware repository methods
- Upgrade golang.org/x/crypto from 0.17.0 to 0.26.0
This commit is contained in:
2026-03-13 09:38:18 +08:00
parent 1216423350
commit cf36b1350d
49 changed files with 2571 additions and 2218 deletions

74
internal/wire/handler.go Normal file
View File

@@ -0,0 +1,74 @@
package wire
import (
"carrot_bbs/internal/cache"
"carrot_bbs/internal/config"
"carrot_bbs/internal/handler"
"carrot_bbs/internal/pkg/sse"
"carrot_bbs/internal/repository"
"carrot_bbs/internal/service"
"github.com/google/wire"
"gorm.io/gorm"
)
// HandlerSet Handler 层 Provider Set
var HandlerSet = wire.NewSet(
// 直接使用构造函数的 Handler
handler.NewUserHandler,
handler.NewCommentHandler,
handler.NewNotificationHandler,
handler.NewUploadHandler,
handler.NewPushHandler,
handler.NewStickerHandler,
handler.NewVoteHandler,
handler.NewScheduleHandler,
// 需要特殊处理的 Handler
ProvidePostHandler,
ProvideMessageHandler,
ProvideSystemMessageHandler,
ProvideGroupHandler,
ProvideGorseHandler,
)
// ProvidePostHandler 提供帖子处理器
func ProvidePostHandler(
postService service.PostService,
userService service.UserService,
) *handler.PostHandler {
return handler.NewPostHandler(postService, userService)
}
// ProvideMessageHandler 提供消息处理器
func ProvideMessageHandler(
chatService service.ChatService,
messageService *service.MessageService,
userService service.UserService,
groupService service.GroupService,
sseHub *sse.Hub,
) *handler.MessageHandler {
return handler.NewMessageHandler(chatService, messageService, userService, groupService, sseHub)
}
// ProvideSystemMessageHandler 提供系统消息处理器
func ProvideSystemMessageHandler(
systemMsgService service.SystemMessageService,
systemNotificationRepo *repository.SystemNotificationRepository,
cacheBackend cache.Cache,
) *handler.SystemMessageHandler {
return handler.NewSystemMessageHandler(systemMsgService, systemNotificationRepo, cacheBackend)
}
// ProvideGroupHandler 提供群组处理器
func ProvideGroupHandler(
groupService service.GroupService,
userService service.UserService,
) *handler.GroupHandler {
return handler.NewGroupHandler(groupService, userService)
}
// ProvideGorseHandler 提供 Gorse 处理器
func ProvideGorseHandler(cfg *config.Config, db *gorm.DB) *handler.GorseHandler {
return handler.NewGorseHandler(cfg.Gorse, db)
}

View File

@@ -0,0 +1,121 @@
package wire
import (
"log"
"time"
"carrot_bbs/internal/cache"
"carrot_bbs/internal/config"
"carrot_bbs/internal/model"
"carrot_bbs/internal/pkg/email"
"carrot_bbs/internal/pkg/gorse"
"carrot_bbs/internal/pkg/openai"
"carrot_bbs/internal/pkg/redis"
"carrot_bbs/internal/pkg/s3"
"carrot_bbs/internal/pkg/sse"
"carrot_bbs/internal/repository"
"github.com/google/wire"
"gorm.io/gorm"
)
// InfrastructureSet 基础设施 Provider Set
var InfrastructureSet = wire.NewSet(
// 配置
ProvideConfig,
// 数据库
ProvideDB,
// 事务管理器
ProvideTransactionManager,
// Redis 和缓存
ProvideRedisClient,
ProvideCache,
// SSE Hub
ProvideSSEHub,
// 外部服务客户端
ProvideGorseClient,
ProvideOpenAIClient,
ProvideEmailClient,
ProvideS3Client,
)
// ProvideConfig 加载配置
func ProvideConfig() (*config.Config, error) {
return config.Load("configs/config.yaml")
}
// ProvideDB 提供数据库连接
func ProvideDB(cfg *config.Config) (*gorm.DB, error) {
return model.NewDB(&cfg.Database)
}
// ProvideRedisClient 提供 Redis 客户端
func ProvideRedisClient(cfg *config.Config) *redis.Client {
client, err := redis.New(&cfg.Redis)
if err != nil {
log.Printf("[WARNING] Failed to connect to Redis: %v, falling back to memory cache", err)
return nil
}
return client
}
// ProvideCache 提供缓存实例
func ProvideCache(cfg *config.Config, redisClient *redis.Client) cache.Cache {
cache.Configure(cache.Settings{
Enabled: cfg.Cache.Enabled,
KeyPrefix: cfg.Cache.KeyPrefix,
DefaultTTL: time.Duration(cfg.Cache.DefaultTTL) * time.Second,
NullTTL: time.Duration(cfg.Cache.NullTTL) * time.Second,
JitterRatio: cfg.Cache.JitterRatio,
PostListTTL: time.Duration(cfg.Cache.Modules.PostList) * time.Second,
ConversationTTL: time.Duration(cfg.Cache.Modules.Conversation) * time.Second,
UnreadCountTTL: time.Duration(cfg.Cache.Modules.UnreadCount) * time.Second,
GroupMembersTTL: time.Duration(cfg.Cache.Modules.GroupMembers) * time.Second,
DisableFlushDB: cfg.Cache.DisableFlushDB,
})
// 直接创建 RedisCache 实例,不依赖全局变量
if redisClient == nil {
log.Println("[Wire] Warning: Redis client is nil, cache will not be available")
return nil
}
cacheInstance := cache.NewRedisCache(redisClient)
log.Println("[Wire] Initialized Redis cache via dependency injection")
return cacheInstance
}
// ProvideSSEHub 提供 SSE Hub
func ProvideSSEHub() *sse.Hub {
return sse.NewHub()
}
// ProvideGorseClient 提供 Gorse 客户端
func ProvideGorseClient(cfg *config.Config) gorse.Client {
return gorse.NewClient(gorse.ConfigFromAppConfig(&cfg.Gorse))
}
// ProvideOpenAIClient 提供 OpenAI 客户端
func ProvideOpenAIClient(cfg *config.Config) openai.Client {
return openai.NewClient(openai.ConfigFromAppConfig(&cfg.OpenAI))
}
// ProvideEmailClient 提供邮件客户端
func ProvideEmailClient(cfg *config.Config) email.Client {
return email.NewClient(email.ConfigFromAppConfig(&cfg.Email))
}
// ProvideS3Client 提供 S3 客户端
func ProvideS3Client(cfg *config.Config) (*s3.Client, error) {
return s3.New(&cfg.S3)
}
// ProvideTransactionManager 提供事务管理器
func ProvideTransactionManager(db *gorm.DB) repository.TransactionManager {
return repository.NewTransactionManager(db)
}

View File

@@ -0,0 +1,11 @@
package wire
import "github.com/google/wire"
// AllSet 包含所有 Provider Set
var AllSet = wire.NewSet(
InfrastructureSet,
RepositorySet,
ServiceSet,
HandlerSet,
)

View File

@@ -0,0 +1,29 @@
package wire
import (
"carrot_bbs/internal/repository"
"github.com/google/wire"
)
// RepositorySet Repository 层 Provider Set
var RepositorySet = wire.NewSet(
repository.NewUserRepository,
repository.NewPostRepository,
repository.NewCommentRepository,
repository.NewMessageRepository,
repository.NewNotificationRepository,
repository.NewPushRecordRepository,
repository.NewDeviceTokenRepository,
repository.NewSystemNotificationRepository,
repository.NewGroupRepository,
repository.NewStickerRepository,
repository.NewVoteRepository,
repository.NewScheduleRepository,
)
// Note: 以下 Repository 返回接口类型而非指针,需要单独处理
// - ScheduleRepository (repository.ScheduleRepository)
// - StickerRepository (repository.StickerRepository)
// - GroupRepository (repository.GroupRepository)
// 这些已经在各自的 New* 函数中正确返回接口类型Wire 可以自动处理

189
internal/wire/service.go Normal file
View File

@@ -0,0 +1,189 @@
package wire
import (
"carrot_bbs/internal/cache"
"carrot_bbs/internal/config"
"carrot_bbs/internal/pkg/email"
"carrot_bbs/internal/pkg/gorse"
"carrot_bbs/internal/pkg/openai"
"carrot_bbs/internal/pkg/s3"
"carrot_bbs/internal/pkg/sse"
"carrot_bbs/internal/repository"
"carrot_bbs/internal/service"
"github.com/google/wire"
"gorm.io/gorm"
)
// ServiceSet Service 层 Provider Set
var ServiceSet = wire.NewSet(
// JWT 服务(需要配置参数)
ProvideJWTService,
// 基础服务
ProvideEmailService,
ProvidePostAIService,
// 核心服务(按依赖顺序)
ProvidePushService,
ProvideSystemMessageService,
ProvidePostService,
ProvideCommentService,
ProvideMessageService,
ProvideNotificationService,
ProvideUserService,
ProvideStickerService,
ProvideVoteService,
ProvideChatService,
ProvideScheduleService,
ProvideGroupService,
ProvideUploadService,
)
// ProvideJWTService 提供 JWT 服务
func ProvideJWTService(cfg *config.Config) *service.JWTService {
return service.NewJWTService(
cfg.JWT.Secret,
int64(cfg.JWT.AccessTokenExpire.Seconds()),
int64(cfg.JWT.RefreshTokenExpire.Seconds()),
)
}
// ProvideEmailService 提供邮件服务
func ProvideEmailService(client email.Client) service.EmailService {
return service.NewEmailService(client)
}
// ProvidePostAIService 提供 AI 服务
func ProvidePostAIService(client openai.Client) *service.PostAIService {
return service.NewPostAIService(client)
}
// ProvidePushService 提供推送服务
func ProvidePushService(
pushRepo *repository.PushRecordRepository,
deviceTokenRepo *repository.DeviceTokenRepository,
messageRepo *repository.MessageRepository,
sseHub *sse.Hub,
) service.PushService {
return service.NewPushService(pushRepo, deviceTokenRepo, messageRepo, sseHub)
}
// ProvideSystemMessageService 提供系统消息服务
func ProvideSystemMessageService(
notifyRepo *repository.SystemNotificationRepository,
pushService service.PushService,
userRepo *repository.UserRepository,
postRepo *repository.PostRepository,
cacheBackend cache.Cache,
) service.SystemMessageService {
return service.NewSystemMessageService(notifyRepo, pushService, userRepo, postRepo, cacheBackend)
}
// ProvidePostService 提供帖子服务
func ProvidePostService(
postRepo *repository.PostRepository,
systemMessageService service.SystemMessageService,
gorseClient gorse.Client,
postAIService *service.PostAIService,
cacheBackend cache.Cache,
txManager repository.TransactionManager,
) service.PostService {
return service.NewPostService(postRepo, systemMessageService, gorseClient, postAIService, cacheBackend, txManager)
}
// ProvideCommentService 提供评论服务
func ProvideCommentService(
commentRepo *repository.CommentRepository,
postRepo *repository.PostRepository,
systemMessageService service.SystemMessageService,
gorseClient gorse.Client,
postAIService *service.PostAIService,
cacheBackend cache.Cache,
) *service.CommentService {
return service.NewCommentService(commentRepo, postRepo, systemMessageService, gorseClient, postAIService, cacheBackend)
}
// ProvideMessageService 提供消息服务
func ProvideMessageService(
db *gorm.DB,
messageRepo *repository.MessageRepository,
cacheBackend cache.Cache,
) *service.MessageService {
return service.NewMessageService(db, messageRepo, cacheBackend)
}
// ProvideNotificationService 提供通知服务
func ProvideNotificationService(
notificationRepo *repository.NotificationRepository,
cacheBackend cache.Cache,
) *service.NotificationService {
return service.NewNotificationService(notificationRepo, cacheBackend)
}
// ProvideUserService 提供用户服务
func ProvideUserService(
userRepo *repository.UserRepository,
systemMessageService service.SystemMessageService,
emailService service.EmailService,
cacheBackend cache.Cache,
) service.UserService {
return service.NewUserService(userRepo, systemMessageService, emailService, cacheBackend)
}
// ProvideStickerService 提供表情服务
func ProvideStickerService(
stickerRepo repository.StickerRepository,
) service.StickerService {
return service.NewStickerService(stickerRepo)
}
// ProvideVoteService 提供投票服务
func ProvideVoteService(
voteRepo *repository.VoteRepository,
postRepo *repository.PostRepository,
cache cache.Cache,
postAIService *service.PostAIService,
systemMessageService service.SystemMessageService,
) *service.VoteService {
return service.NewVoteService(voteRepo, postRepo, cache, postAIService, systemMessageService)
}
// ProvideChatService 提供聊天服务
// Note: sensitiveService 传 nil与 main.go 保持一致
func ProvideChatService(
db *gorm.DB,
messageRepo *repository.MessageRepository,
userRepo *repository.UserRepository,
sseHub *sse.Hub,
cacheBackend cache.Cache,
) service.ChatService {
return service.NewChatService(db, messageRepo, userRepo, nil, sseHub, cacheBackend)
}
// ProvideScheduleService 提供日程服务
func ProvideScheduleService(
scheduleRepo repository.ScheduleRepository,
) service.ScheduleService {
return service.NewScheduleService(scheduleRepo)
}
// ProvideGroupService 提供群组服务
func ProvideGroupService(
db *gorm.DB,
groupRepo repository.GroupRepository,
userRepo *repository.UserRepository,
messageRepo *repository.MessageRepository,
sseHub *sse.Hub,
cacheBackend cache.Cache,
) service.GroupService {
return service.NewGroupService(db, groupRepo, userRepo, messageRepo, sseHub, cacheBackend)
}
// ProvideUploadService 提供上传服务
func ProvideUploadService(
s3Client *s3.Client,
userService service.UserService,
) *service.UploadService {
return service.NewUploadService(s3Client, userService)
}