Files
backend/internal/model/init.go
lan d3065b30cc feat(admin): add comprehensive admin management system
- Add admin handlers and services for users, posts, comments, groups, and dashboard
- Implement role permission management with ability to view and update permissions
- Add database seeding for roles and Casbin permission policies
- Upgrade Casbin from v2 to v3 with GORM adapter for persistent policy storage
- Add admin-specific repository methods with filtering and pagination
- Register new admin API routes under /api/v1/admin/*
2026-03-14 18:01:55 +08:00

289 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package model
import (
"fmt"
"log"
"os"
"time"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"carrot_bbs/internal/config"
)
// NewDB 创建数据库连接(用于 Wire 依赖注入)
func NewDB(cfg *config.DatabaseConfig) (*gorm.DB, error) {
var err error
var db *gorm.DB
gormLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Duration(cfg.SlowThresholdMs) * time.Millisecond,
LogLevel: parseGormLogLevel(cfg.LogLevel),
IgnoreRecordNotFoundError: cfg.IgnoreRecordNotFound,
ParameterizedQueries: cfg.ParameterizedQueries,
Colorful: false,
},
)
// 根据数据库类型选择驱动
switch cfg.Type {
case "sqlite":
db, err = gorm.Open(sqlite.Open(cfg.SQLite.Path), &gorm.Config{
Logger: gormLogger,
})
case "postgres", "postgresql":
dsn := cfg.Postgres.DSN()
db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: gormLogger,
})
default:
// 默认使用PostgreSQL
dsn := cfg.Postgres.DSN()
db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: gormLogger,
})
}
if err != nil {
return nil, fmt.Errorf("failed to connect to database: %w", err)
}
// 配置连接池SQLite不支持连接池配置跳过
if cfg.Type != "sqlite" {
sqlDB, err := db.DB()
if err != nil {
return nil, fmt.Errorf("failed to get database instance: %w", err)
}
sqlDB.SetMaxIdleConns(cfg.MaxIdleConns)
sqlDB.SetMaxOpenConns(cfg.MaxOpenConns)
}
// 自动迁移
if err := autoMigrate(db); err != nil {
return nil, fmt.Errorf("failed to auto migrate: %w", err)
}
log.Printf("Database connected (%s) and migrated successfully", cfg.Type)
return db, nil
}
func parseGormLogLevel(level string) logger.LogLevel {
switch level {
case "silent":
return logger.Silent
case "error":
return logger.Error
case "warn":
return logger.Warn
case "info":
return logger.Info
default:
return logger.Warn
}
}
// autoMigrate 自动迁移数据库表
func autoMigrate(db *gorm.DB) error {
err := db.AutoMigrate(
// 用户相关
&User{},
// 帖子相关
&Post{},
&PostImage{},
// 评论相关
&Comment{},
&CommentLike{},
// 消息相关使用雪花算法ID和seq机制
// 已读位置存储在 ConversationParticipant.LastReadSeq 中
&Conversation{},
&ConversationParticipant{},
&Message{},
// 系统通知(独立表,每个用户只能看到自己的通知)
&SystemNotification{},
// 通知
&Notification{},
// 推送中心相关
&PushRecord{}, // 推送记录
&DeviceToken{}, // 设备Token
// 社交
&Follow{},
&UserBlock{},
&PostLike{},
&Favorite{},
// 投票
&VoteOption{},
&UserVote{},
// 敏感词和审核
&SensitiveWord{},
&AuditLog{},
// 群组相关
&Group{},
&GroupMember{},
&GroupAnnouncement{},
&GroupJoinRequest{},
// 自定义表情
&UserSticker{},
// 课表
&ScheduleCourse{},
// 用户活跃相关
&UserActiveLog{},
&UserActivityStat{},
// 角色和权限相关
&Role{},
&UserRole{},
&CasbinRule{},
)
if err != nil {
return err
}
// 初始化角色种子数据
if err := seedRoles(db); err != nil {
return fmt.Errorf("failed to seed roles: %w", err)
}
// 初始化权限策略种子数据
if err := seedPermissions(db); err != nil {
return fmt.Errorf("failed to seed permissions: %w", err)
}
return nil
}
// seedRoles 初始化角色种子数据
func seedRoles(db *gorm.DB) error {
// 检查是否已有角色数据
var count int64
if err := db.Model(&Role{}).Count(&count).Error; err != nil {
return err
}
// 如果已有数据,跳过种子初始化
if count > 0 {
return nil
}
// 预定义角色数据
roles := []Role{
{
Name: RoleSuperAdmin,
DisplayName: "超级管理员",
Description: "拥有系统最高权限,可以管理所有用户和内容",
Priority: 100,
},
{
Name: RoleAdmin,
DisplayName: "管理员",
Description: "系统管理员,可以管理用户和内容",
Priority: 80,
},
{
Name: RoleModerator,
DisplayName: "版主",
Description: "内容审核员,可以审核和管理内容",
Priority: 60,
},
{
Name: RoleUser,
DisplayName: "普通用户",
Description: "注册用户,拥有基本权限",
Priority: 40,
},
{
Name: RoleBanned,
DisplayName: "被封禁用户",
Description: "被禁止访问的用户",
Priority: 0,
},
}
// 批量插入角色
if err := db.Create(&roles).Error; err != nil {
return err
}
log.Printf("Seeded %d roles successfully", len(roles))
return nil
}
// seedPermissions 初始化权限策略种子数据
func seedPermissions(db *gorm.DB) error {
// 检查是否已有权限策略数据
var count int64
if err := db.Model(&CasbinRule{}).Where("ptype = ?", "p").Count(&count).Error; err != nil {
return err
}
// 如果已有数据,跳过种子初始化
if count > 0 {
return nil
}
// 预定义权限策略数据
// p = sub, obj, act (角色, 资源, 操作)
permissions := []CasbinRule{
// 超级管理员 - 拥有所有权限
{Ptype: "p", V0: RoleSuperAdmin, V1: "/*", V2: "*"},
// 管理员 - 拥有管理权限
{Ptype: "p", V0: RoleAdmin, V1: "/api/v1/admin/*", V2: "*"},
{Ptype: "p", V0: RoleAdmin, V1: "/api/v1/users/*", V2: "GET"},
{Ptype: "p", V0: RoleAdmin, V1: "/api/v1/posts/*", V2: "*"},
{Ptype: "p", V0: RoleAdmin, V1: "/api/v1/comments/*", V2: "*"},
{Ptype: "p", V0: RoleAdmin, V1: "/api/v1/groups/*", V2: "*"},
// 版主 - 拥有内容审核权限
{Ptype: "p", V0: RoleModerator, V1: "/api/v1/posts/*", V2: "GET"},
{Ptype: "p", V0: RoleModerator, V1: "/api/v1/posts/*", V2: "DELETE"},
{Ptype: "p", V0: RoleModerator, V1: "/api/v1/comments/*", V2: "GET"},
{Ptype: "p", V0: RoleModerator, V1: "/api/v1/comments/*", V2: "DELETE"},
{Ptype: "p", V0: RoleModerator, V1: "/api/v1/groups/*", V2: "GET"},
// 普通用户 - 拥有基本权限
{Ptype: "p", V0: RoleUser, V1: "/api/v1/posts", V2: "GET"},
{Ptype: "p", V0: RoleUser, V1: "/api/v1/posts/*", V2: "GET"},
{Ptype: "p", V0: RoleUser, V1: "/api/v1/posts", V2: "POST"},
{Ptype: "p", V0: RoleUser, V1: "/api/v1/comments/*", V2: "GET"},
{Ptype: "p", V0: RoleUser, V1: "/api/v1/comments", V2: "POST"},
{Ptype: "p", V0: RoleUser, V1: "/api/v1/users/me", V2: "GET"},
{Ptype: "p", V0: RoleUser, V1: "/api/v1/users/me", V2: "PUT"},
{Ptype: "p", V0: RoleUser, V1: "/api/v1/groups", V2: "GET"},
{Ptype: "p", V0: RoleUser, V1: "/api/v1/groups/*", V2: "GET"},
// 被封禁用户 - 无权限
}
// 批量插入权限策略
if err := db.Create(&permissions).Error; err != nil {
return err
}
log.Printf("Seeded %d permissions successfully", len(permissions))
return nil
}
// CloseDB 关闭数据库连接
func CloseDB(db *gorm.DB) {
if sqlDB, err := db.DB(); err == nil {
sqlDB.Close()
}
}