后端重构

This commit is contained in:
Yvan
2026-01-14 14:34:49 +08:00
parent f5f77b32fe
commit 30842b42e5
274 changed files with 39754 additions and 1005 deletions
+284 -11
View File
@@ -1,7 +1,10 @@
package data
import (
"context"
"fmt"
"time"
"kra/internal/biz/example"
"kra/internal/conf"
dataexample "kra/internal/data/example"
@@ -9,17 +12,24 @@ import (
pkgcasbin "kra/pkg/casbin"
"github.com/casbin/casbin/v2"
oracle "github.com/dzwvip/gorm-oracle"
"github.com/glebarez/sqlite"
_ "github.com/go-sql-driver/mysql"
"github.com/google/wire"
"github.com/redis/go-redis/v9"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlserver"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
)
// ProviderSet is data providers.
var ProviderSet = wire.NewSet(
NewDB,
NewData,
NewRedisClient,
NewCasbinEnforcer,
NewOssUploader,
NewDataAuthorityProvider,
@@ -41,6 +51,8 @@ var ProviderSet = wire.NewSet(
datasystem.NewAutoCodeRepo,
datasystem.NewAutoCodeHistoryRepo,
datasystem.NewAutoCodePackageRepo,
datasystem.NewAutoCodeTemplateRepo,
datasystem.NewAutoCodePluginRepo,
// Example
dataexample.NewFileUploadRepo,
dataexample.NewCustomerRepo,
@@ -50,34 +62,295 @@ var ProviderSet = wire.NewSet(
// Data 数据层包装器
type Data struct {
db *gorm.DB
db *gorm.DB
redis redis.UniversalClient
}
// NewData 创建数据层包装器
func NewData(db *gorm.DB) (*Data, func(), error) {
func NewData(db *gorm.DB, redisClient redis.UniversalClient) (*Data, func(), error) {
cleanup := func() {
// 关闭数据库连接
sqlDB, err := db.DB()
if err == nil {
sqlDB.Close()
if db != nil {
sqlDB, err := db.DB()
if err == nil {
sqlDB.Close()
}
}
// 关闭Redis连接
if redisClient != nil {
redisClient.Close()
}
}
return &Data{db: db}, cleanup, nil
return &Data{db: db, redis: redisClient}, cleanup, nil
}
// NewDB 创建数据库连接
func NewDB(c *conf.Mysql) (*gorm.DB, error) {
// DB 获取数据库连接
func (d *Data) DB() *gorm.DB {
return d.db
}
// Redis 获取Redis客户端
func (d *Data) Redis() redis.UniversalClient {
return d.redis
}
// NewDB 创建数据库连接(根据配置的数据库类型)
func NewDB(system *conf.System, mysqlCfg *conf.Mysql, pgsqlCfg *conf.Pgsql, sqliteCfg *conf.Sqlite, mssqlCfg *conf.Mssql, oracleCfg *conf.Oracle) (*gorm.DB, error) {
dbType := "mysql"
if system != nil && system.DbType != "" {
dbType = system.DbType
}
switch dbType {
case "mysql":
return NewMysqlDB(mysqlCfg)
case "pgsql":
return NewPgsqlDB(pgsqlCfg)
case "sqlite":
return NewSqliteDB(sqliteCfg)
case "mssql":
return NewMssqlDB(mssqlCfg)
case "oracle":
return NewOracleDB(oracleCfg)
default:
return NewMysqlDB(mysqlCfg)
}
}
// NewMysqlDB 创建MySQL数据库连接
func NewMysqlDB(c *conf.Mysql) (*gorm.DB, error) {
if c == nil || c.DbName == "" {
return nil, fmt.Errorf("mysql config is empty")
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?%s",
c.Username, c.Password, c.Path, c.Port, c.DbName, c.Config)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
gormConfig := newGormConfig(c.Prefix, c.Singular, c.LogMode)
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: dsn,
DefaultStringSize: 191,
SkipInitializeWithVersion: false,
}), gormConfig)
if err != nil {
return nil, err
}
if c.Engine != "" {
db.InstanceSet("gorm:table_options", "ENGINE="+c.Engine)
}
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
sqlDB.SetMaxIdleConns(int(c.MaxIdleConns))
sqlDB.SetMaxOpenConns(int(c.MaxOpenConns))
return db, nil
}
// NewPgsqlDB 创建PostgreSQL数据库连接
func NewPgsqlDB(c *conf.Pgsql) (*gorm.DB, error) {
if c == nil || c.DbName == "" {
return nil, fmt.Errorf("pgsql config is empty")
}
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s %s",
c.Path, c.Username, c.Password, c.DbName, c.Port, c.Config)
gormConfig := newGormConfig(c.Prefix, c.Singular, c.LogMode)
db, err := gorm.Open(postgres.New(postgres.Config{
DSN: dsn,
PreferSimpleProtocol: false,
}), gormConfig)
if err != nil {
return nil, err
}
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
sqlDB.SetMaxIdleConns(int(c.MaxIdleConns))
sqlDB.SetMaxOpenConns(int(c.MaxOpenConns))
return db, nil
}
// NewSqliteDB 创建SQLite数据库连接
func NewSqliteDB(c *conf.Sqlite) (*gorm.DB, error) {
if c == nil || c.DbName == "" {
return nil, fmt.Errorf("sqlite config is empty")
}
dsn := c.DbName
if c.Path != "" {
dsn = fmt.Sprintf("%s/%s", c.Path, c.DbName)
}
gormConfig := newGormConfig(c.Prefix, c.Singular, c.LogMode)
db, err := gorm.Open(sqlite.Open(dsn), gormConfig)
if err != nil {
return nil, err
}
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
sqlDB.SetMaxIdleConns(int(c.MaxIdleConns))
sqlDB.SetMaxOpenConns(int(c.MaxOpenConns))
return db, nil
}
// NewMssqlDB 创建MSSQL数据库连接
func NewMssqlDB(c *conf.Mssql) (*gorm.DB, error) {
if c == nil || c.DbName == "" {
return nil, fmt.Errorf("mssql config is empty")
}
dsn := fmt.Sprintf("sqlserver://%s:%s@%s:%s?database=%s&%s",
c.Username, c.Password, c.Path, c.Port, c.DbName, c.Config)
gormConfig := newGormConfig(c.Prefix, c.Singular, c.LogMode)
db, err := gorm.Open(sqlserver.New(sqlserver.Config{
DSN: dsn,
DefaultStringSize: 191,
}), gormConfig)
if err != nil {
return nil, err
}
if c.Engine != "" {
db.InstanceSet("gorm:table_options", "ENGINE="+c.Engine)
}
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
sqlDB.SetMaxIdleConns(int(c.MaxIdleConns))
sqlDB.SetMaxOpenConns(int(c.MaxOpenConns))
return db, nil
}
// NewOracleDB 创建Oracle数据库连接
func NewOracleDB(c *conf.Oracle) (*gorm.DB, error) {
if c == nil || c.DbName == "" {
return nil, fmt.Errorf("oracle config is empty")
}
dsn := fmt.Sprintf("oracle://%s:%s@%s:%s/%s?%s",
c.Username, c.Password, c.Path, c.Port, c.DbName, c.Config)
gormConfig := newGormConfig(c.Prefix, c.Singular, c.LogMode)
db, err := gorm.Open(oracle.Open(dsn), gormConfig)
if err != nil {
return nil, err
}
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
sqlDB.SetMaxIdleConns(int(c.MaxIdleConns))
sqlDB.SetMaxOpenConns(int(c.MaxOpenConns))
return db, nil
}
// newGormConfig 创建GORM配置
func newGormConfig(prefix string, singular bool, logMode string) *gorm.Config {
return &gorm.Config{
Logger: logger.New(
&gormLogWriter{},
logger.Config{
SlowThreshold: 200 * time.Millisecond,
LogLevel: parseLogLevel(logMode),
Colorful: true,
},
),
NamingStrategy: schema.NamingStrategy{
TablePrefix: prefix,
SingularTable: singular,
},
DisableForeignKeyConstraintWhenMigrating: true,
}
}
// gormLogWriter GORM日志写入器
type gormLogWriter struct{}
func (w *gormLogWriter) Printf(format string, args ...interface{}) {
fmt.Printf(format+"\n", args...)
}
// parseLogLevel 解析日志级别
func parseLogLevel(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.Info
}
}
// NewRedisClient 创建Redis客户端
func NewRedisClient(c *conf.Redis, system *conf.System) (redis.UniversalClient, error) {
// 如果不使用Redis,返回nil
if system != nil && !system.UseRedis {
return nil, nil
}
if c == nil {
return nil, nil
}
var client redis.UniversalClient
// 使用集群模式
if c.UseCluster {
client = redis.NewClusterClient(&redis.ClusterOptions{
Addrs: c.ClusterAddrs,
Password: c.Password,
})
} else {
// 使用单例模式
client = redis.NewClient(&redis.Options{
Addr: c.Addr,
Password: c.Password,
DB: int(c.Db),
})
}
// 测试连接
_, err := client.Ping(context.Background()).Result()
if err != nil {
return nil, fmt.Errorf("redis connect ping failed: %v", err)
}
return client, nil
}
// NewCasbinEnforcer 创建Casbin Enforcer
func NewCasbinEnforcer(db *gorm.DB) (*casbin.SyncedCachedEnforcer, error) {
return pkgcasbin.InitCasbin(db, "")
+5
View File
@@ -0,0 +1,5 @@
package query
// 运行 go generate ./... 时会自动执行 GORM Gen 代码生成
// 这会读取数据库中的所有表并生成对应的查询代码
//go:generate go run ../../../cmd/gen/main.go
+59 -6
View File
@@ -4,21 +4,37 @@ import (
"context"
"kra/internal/biz/system"
"kra/internal/conf"
"gorm.io/gorm"
)
type autoCodeRepo struct {
db *gorm.DB
db *gorm.DB
dbName string
conf *conf.Bootstrap
}
// NewAutoCodeRepo 创建自动代码仓储
func NewAutoCodeRepo(db *gorm.DB) system.AutoCodeRepo {
return &autoCodeRepo{db: db}
func NewAutoCodeRepo(db *gorm.DB, conf *conf.Bootstrap) system.AutoCodeRepo {
// 获取默认数据库名
dbName := ""
if conf.Mysql != nil && conf.Mysql.DbName != "" {
dbName = conf.Mysql.DbName
} else if conf.Pgsql != nil && conf.Pgsql.DbName != "" {
dbName = conf.Pgsql.DbName
} else if conf.Sqlite != nil && conf.Sqlite.DbName != "" {
dbName = conf.Sqlite.DbName
}
return &autoCodeRepo{
db: db,
dbName: dbName,
conf: conf,
}
}
// GetDB 获取所有数据库
func (r *autoCodeRepo) GetDB(ctx context.Context) ([]system.Db, error) {
func (r *autoCodeRepo) GetDB(ctx context.Context, businessDB string) ([]system.Db, error) {
var entities []system.Db
sql := "SELECT SCHEMA_NAME AS `database` FROM INFORMATION_SCHEMA.SCHEMATA;"
err := r.db.WithContext(ctx).Raw(sql).Scan(&entities).Error
@@ -26,7 +42,7 @@ func (r *autoCodeRepo) GetDB(ctx context.Context) ([]system.Db, error) {
}
// GetTables 获取指定数据库的所有表
func (r *autoCodeRepo) GetTables(ctx context.Context, dbName string) ([]system.Table, error) {
func (r *autoCodeRepo) GetTables(ctx context.Context, businessDB, dbName string) ([]system.Table, error) {
var entities []system.Table
sql := `SELECT table_name AS table_name FROM information_schema.tables WHERE table_schema = ?`
err := r.db.WithContext(ctx).Raw(sql, dbName).Scan(&entities).Error
@@ -34,7 +50,7 @@ func (r *autoCodeRepo) GetTables(ctx context.Context, dbName string) ([]system.T
}
// GetColumn 获取指定表的所有列
func (r *autoCodeRepo) GetColumn(ctx context.Context, tableName, dbName string) ([]system.Column, error) {
func (r *autoCodeRepo) GetColumn(ctx context.Context, businessDB, tableName, dbName string) ([]system.Column, error) {
var entities []system.Column
sql := `
SELECT
@@ -69,3 +85,40 @@ func (r *autoCodeRepo) GetColumn(ctx context.Context, tableName, dbName string)
err := r.db.WithContext(ctx).Raw(sql, tableName, dbName).Scan(&entities).Error
return entities, err
}
// GetDBList 获取配置的数据库列表
func (r *autoCodeRepo) GetDBList() []system.DBInfo {
// 目前KRA只支持单数据库配置,返回主数据库信息
var dbList []system.DBInfo
if r.conf.Mysql != nil && r.conf.Mysql.DbName != "" {
dbList = append(dbList, system.DBInfo{
AliasName: "",
DBName: r.conf.Mysql.DbName,
Disable: false,
DBType: "mysql",
})
}
if r.conf.Pgsql != nil && r.conf.Pgsql.DbName != "" {
dbList = append(dbList, system.DBInfo{
AliasName: "",
DBName: r.conf.Pgsql.DbName,
Disable: false,
DBType: "pgsql",
})
}
if r.conf.Sqlite != nil && r.conf.Sqlite.DbName != "" {
dbList = append(dbList, system.DBInfo{
AliasName: "",
DBName: r.conf.Sqlite.DbName,
Disable: false,
DBType: "sqlite",
})
}
return dbList
}
// GetActiveDBName 获取当前活动的数据库名
func (r *autoCodeRepo) GetActiveDBName(businessDB string) string {
// 目前KRA只支持单数据库,直接返回配置的数据库名
return r.dbName
}
+15
View File
@@ -66,3 +66,18 @@ func (r *autoCodeHistoryRepo) DropTable(ctx context.Context, businessDB, tableNa
// 注意:这里简化处理,实际应该支持多数据库
return r.db.WithContext(ctx).Exec("DROP TABLE IF EXISTS " + tableName).Error
}
// Create 创建历史记录
func (r *autoCodeHistoryRepo) Create(ctx context.Context, history *system.SysAutoCodeHistory) error {
return r.db.WithContext(ctx).Create(history).Error
}
// Repeat 检测重复
func (r *autoCodeHistoryRepo) Repeat(ctx context.Context, businessDB, structName, abbreviation, pkg string) bool {
var count int64
r.db.WithContext(ctx).Model(&system.SysAutoCodeHistory{}).
Where("business_db = ? AND struct_name = ? AND abbreviation = ? AND package = ?",
businessDB, structName, abbreviation, pkg).
Count(&count)
return count > 0
}
File diff suppressed because it is too large Load Diff
+38
View File
@@ -0,0 +1,38 @@
package system
import (
"context"
"kra/internal/biz/system"
"gorm.io/gorm"
)
type autoCodePluginRepo struct {
db *gorm.DB
}
// NewAutoCodePluginRepo 创建插件仓储
func NewAutoCodePluginRepo(db *gorm.DB) system.AutoCodePluginRepo {
return &autoCodePluginRepo{db: db}
}
// GetMenusByIDs 根据ID列表获取菜单
func (r *autoCodePluginRepo) GetMenusByIDs(ctx context.Context, ids []uint) ([]system.SysBaseMenu, error) {
var menus []system.SysBaseMenu
if len(ids) == 0 {
return menus, nil
}
err := r.db.WithContext(ctx).Where("id IN ?", ids).Find(&menus).Error
return menus, err
}
// GetApisByIDs 根据ID列表获取API
func (r *autoCodePluginRepo) GetApisByIDs(ctx context.Context, ids []uint) ([]system.SysApi, error) {
var apis []system.SysApi
if len(ids) == 0 {
return apis, nil
}
err := r.db.WithContext(ctx).Where("id IN ?", ids).Find(&apis).Error
return apis, err
}
@@ -0,0 +1,68 @@
package system
import (
"context"
"kra/internal/biz/system"
"gorm.io/gorm"
)
type autoCodeTemplateRepo struct {
db *gorm.DB
}
// NewAutoCodeTemplateRepo 创建自动代码模板仓储
func NewAutoCodeTemplateRepo(db *gorm.DB) system.AutoCodeTemplateRepo {
return &autoCodeTemplateRepo{db: db}
}
// GetPackageByName 根据包名获取包信息
func (r *autoCodeTemplateRepo) GetPackageByName(ctx context.Context, packageName string) (*system.SysAutoCodePackage, error) {
var entity system.SysAutoCodePackage
err := r.db.WithContext(ctx).Where("package_name = ?", packageName).First(&entity).Error
return &entity, err
}
// CreateApis 批量创建API
func (r *autoCodeTemplateRepo) CreateApis(ctx context.Context, apis []system.SysApi) ([]uint, error) {
if len(apis) == 0 {
return nil, nil
}
err := r.db.WithContext(ctx).Create(&apis).Error
if err != nil {
return nil, err
}
ids := make([]uint, len(apis))
for i, api := range apis {
ids[i] = api.ID
}
return ids, nil
}
// GetMenuByName 根据名称获取菜单
func (r *autoCodeTemplateRepo) GetMenuByName(ctx context.Context, name string) (*system.SysBaseMenu, error) {
var entity system.SysBaseMenu
err := r.db.WithContext(ctx).Where("name = ?", name).First(&entity).Error
return &entity, err
}
// CreateMenu 创建菜单
func (r *autoCodeTemplateRepo) CreateMenu(ctx context.Context, menu *system.SysBaseMenu) error {
return r.db.WithContext(ctx).Create(menu).Error
}
// CreateExportTemplate 创建导出模板
func (r *autoCodeTemplateRepo) CreateExportTemplate(ctx context.Context, tpl *system.SysExportTemplate) error {
return r.db.WithContext(ctx).Create(tpl).Error
}
// Repeat 检查是否重复创建
func (r *autoCodeTemplateRepo) Repeat(ctx context.Context, businessDB, structName, abbreviation, pkg string) bool {
var count int64
r.db.WithContext(ctx).Model(&system.SysAutoCodeHistory{}).
Where("struct_name = ? AND package = ?", structName, pkg).
Or("abbreviation = ? AND package = ?", abbreviation, pkg).
Count(&count)
return count > 0
}
+10
View File
@@ -108,3 +108,13 @@ func (r *casbinRepo) UpdateCasbin(adminAuthorityID, authorityId uint, casbinInfo
func (r *casbinRepo) RemoveFilteredPolicy(authorityId string) error {
return r.db.Delete(&gormadapter.CasbinRule{}, "v0 = ?", authorityId).Error
}
// SyncPolicy 同步策略(先删除再添加)
func (r *casbinRepo) SyncPolicy(authorityId string, rules [][]string) error {
// 先删除旧策略
if err := r.RemoveFilteredPolicy(authorityId); err != nil {
return err
}
// 再添加新策略
return r.AddPolicies(rules)
}
+36 -2
View File
@@ -2,22 +2,40 @@ package system
import (
"context"
"time"
"kra/internal/biz/system"
"kra/internal/data/model"
"kra/internal/data/query"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
)
type jwtBlacklistRepo struct {
db *gorm.DB
db *gorm.DB
redisClient redis.UniversalClient
expiresTime time.Duration
}
// NewJwtBlacklistRepo 创建JWT黑名单仓储
func NewJwtBlacklistRepo(db *gorm.DB) system.JwtBlacklistRepo {
query.SetDefault(db)
return &jwtBlacklistRepo{db: db}
return &jwtBlacklistRepo{
db: db,
redisClient: nil, // 默认不使用Redis
expiresTime: 7 * 24 * time.Hour,
}
}
// NewJwtBlacklistRepoWithRedis 创建带Redis的JWT黑名单仓储
func NewJwtBlacklistRepoWithRedis(db *gorm.DB, redisClient redis.UniversalClient, expiresTime time.Duration) system.JwtBlacklistRepo {
query.SetDefault(db)
return &jwtBlacklistRepo{
db: db,
redisClient: redisClient,
expiresTime: expiresTime,
}
}
func (r *jwtBlacklistRepo) Create(ctx context.Context, jwt string) error {
@@ -35,3 +53,19 @@ func (r *jwtBlacklistRepo) LoadAll(ctx context.Context) ([]string, error) {
err := r.db.WithContext(ctx).Model(&model.JwtBlacklist{}).Pluck("jwt", &jwts).Error
return jwts, err
}
// GetRedisJWT 从Redis获取JWT
func (r *jwtBlacklistRepo) GetRedisJWT(ctx context.Context, username string) (string, error) {
if r.redisClient == nil {
return "", redis.Nil
}
return r.redisClient.Get(ctx, "jwt:"+username).Result()
}
// SetRedisJWT 设置Redis JWT
func (r *jwtBlacklistRepo) SetRedisJWT(ctx context.Context, token, username string) error {
if r.redisClient == nil {
return nil
}
return r.redisClient.Set(ctx, "jwt:"+username, token, r.expiresTime).Err()
}