## CI 修复 (P0/P1) P0 — 编译阻塞: - interfaces.go: postUseCase ISP 接口移除不用的 Create/Update/Delete P1 — 必须修复: - ST1000: 为 13 个包添加包注释 (common/config/model/service/...) - ST1005: redis_store.go 全部错误消息改为小写开头 - errcheck (19处): defer Close()→闭包忽略, notifier.Create→_=, r.Run→检查error - errorlint (9处): switch-on-error→errors.Is 链, ==→errors.Is - ST1020/ST1022: 导出符号注释以符号名开头 P2 — 安全评审: - gosec (12处): G203/G301/G304/G306 添加 nolint 注释并附理由 P3 — 清理: - unused: 移除 hasUnicode/energyStore/current/energyAdminUseCase - gofumpt + goimports 全量格式化 (35+ 文件)
113 lines
4.0 KiB
Go
113 lines
4.0 KiB
Go
package main
|
||
|
||
import (
|
||
"log"
|
||
"time"
|
||
|
||
"metazone.cc/mce/internal/common"
|
||
"metazone.cc/mce/internal/config"
|
||
"metazone.cc/mce/internal/model"
|
||
"metazone.cc/mce/internal/repository"
|
||
"metazone.cc/mce/internal/router"
|
||
"metazone.cc/mce/internal/scheduler"
|
||
"metazone.cc/mce/internal/theme"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/redis/go-redis/v9"
|
||
"gorm.io/driver/postgres"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
func main() {
|
||
// 配置
|
||
cfg := config.Load("config.yaml")
|
||
|
||
// 从配置初始化角色系统(OCP:新增角色无需修改源代码)
|
||
model.InitRoles(cfg.Roles.Levels, cfg.Roles.Names, cfg.Roles.Permissions.OperableRoles)
|
||
|
||
// 数据库
|
||
db, err := gorm.Open(postgres.Open(cfg.Database.DSN()), &gorm.Config{})
|
||
if err != nil {
|
||
log.Fatalf("连接数据库失败: %v", err)
|
||
}
|
||
if err := db.AutoMigrate(&model.User{}, &model.AuditSubmission{}, &model.SiteSetting{}, &model.Notification{}, &model.Post{}, &model.PostReadLog{}, &model.PostGuestReadLog{}, &model.UserCheckIn{}, &model.UserTask{}, &model.EnergyLog{}, &model.PostEnergizeLog{}, &model.DailyExpSummary{}, &model.Comment{}, &model.CommentMention{}, &model.CommunityFund{}, &model.FundLog{}, &model.PostLike{}, &model.PostDislike{}, &model.UserFollow{}, &model.DailyLikeSummary{}, &model.Folder{}, &model.FolderItem{}, &model.Announcement{}, &model.Category{}, &model.Tag{}, &model.PostTag{}); err != nil {
|
||
log.Fatalf("数据库迁移失败: %v", err)
|
||
}
|
||
|
||
// 初始化公户行(id=1, balance=0,如已存在则忽略)
|
||
if err := db.FirstOrCreate(&model.CommunityFund{ID: 1, Balance: 0}).Error; err != nil {
|
||
log.Fatalf("初始化公户失败: %v", err)
|
||
}
|
||
|
||
// 邮箱唯一索引迁移:从全表唯一改为部分唯一(仅 deleted_at IS NULL 的行)
|
||
// 支持软删除用户邮箱复用
|
||
migrations := []string{
|
||
`DROP INDEX IF EXISTS idx_users_email`,
|
||
`CREATE INDEX IF NOT EXISTS idx_users_email ON users (email)`,
|
||
`CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email_active ON users (email) WHERE deleted_at IS NULL`,
|
||
}
|
||
for _, m := range migrations {
|
||
if err := db.Exec(m).Error; err != nil {
|
||
log.Fatalf("数据库迁移失败 (%s): %v", m, err)
|
||
}
|
||
}
|
||
log.Println("数据库连接成功")
|
||
|
||
// Gin 模式
|
||
gin.SetMode(cfg.Server.Mode)
|
||
|
||
r := gin.Default()
|
||
|
||
// 计算静态资源哈希(用于缓存破坏,文件不变哈希不变)
|
||
common.ComputeAssetHashes("templates/MetaLab-2026")
|
||
|
||
// 模板加载(前端主题 + 管理后台)
|
||
tmpl, err := theme.LoadTemplates(
|
||
theme.TemplateRoot{Dir: "templates/MetaLab-2026/html", Prefix: ""},
|
||
theme.TemplateRoot{Dir: "templates/admin/html", Prefix: "admin/"},
|
||
)
|
||
if err != nil {
|
||
panic("加载模板失败: " + err.Error())
|
||
}
|
||
r.SetHTMLTemplate(tmpl)
|
||
|
||
// 静态资源
|
||
r.Static("/static", "./templates/MetaLab-2026/static")
|
||
r.Static("/admin/static", "./templates/admin/static")
|
||
r.Static("/shared/static", "./templates/shared/static")
|
||
r.Static("/uploads/avatars", "./storage/uploads/avatars")
|
||
r.Static("/uploads/posts", "./storage/uploads/posts")
|
||
|
||
// Redis 客户端(可选,不可达时 FallbackStore 自动降级,恢复后自动切回)
|
||
var redisClient *redis.Client
|
||
if cfg.Redis.Enabled {
|
||
redisClient, err = common.NewRedisClient(cfg.Redis)
|
||
if err != nil {
|
||
log.Printf("Redis 不可达,启动后首次请求将自动降级,后续恢复自动切回: %v", err)
|
||
// 不置 nil:go-redis 自带重连,FallbackStore 负责健康检测与恢复
|
||
} else {
|
||
log.Printf("Redis 已连接 %s:%s (DB=%d)", cfg.Redis.Host, cfg.Redis.Port, cfg.Redis.DB)
|
||
}
|
||
} else {
|
||
log.Println("Redis 未启用,使用内存存储")
|
||
}
|
||
|
||
// 站点设置管理器(DB 持久化 + 内存缓存)
|
||
siteSettings, err := config.NewSiteSettings(db)
|
||
if err != nil {
|
||
log.Fatalf("初始化站点设置失败: %v", err)
|
||
}
|
||
|
||
// 路由注册
|
||
router.Setup(r, db, cfg, siteSettings, redisClient)
|
||
|
||
// 定时发布调度器
|
||
scheduler.StartPublishScheduler(repository.NewPostRepo(db), 1*time.Minute)
|
||
|
||
// 启动
|
||
log.Printf("MetaZone.FAN 启动于 0.0.0.0:%s", cfg.Server.Port)
|
||
if err := r.Run("0.0.0.0:" + cfg.Server.Port); err != nil {
|
||
log.Fatalf("服务启动失败: %v", err)
|
||
}
|
||
}
|