feat():learning后台管理项目初始化

This commit is contained in:
yuj
2025-12-04 16:23:46 +08:00
parent 39886d50d2
commit 88e048f4d1
154 changed files with 28966 additions and 6 deletions

322
internal/config/config.go Normal file
View File

@@ -0,0 +1,322 @@
package config
import (
"fmt"
"sync"
"time"
"github.com/spf13/viper"
"gorm.io/driver/mysql"
)
// DatabaseConfig 数据库配置
type DatabaseConfig struct {
DSN string `mapstructure:"dsn"`
MaxIdleConns int `mapstructure:"maxIdleConns"`
MaxOpenConns int `mapstructure:"maxOpenConns"`
LogLevel string `mapstructure:"logLevel"`
}
// ServerConfig 服务器配置
type ServerConfig struct {
Addr string `mapstructure:"addr"`
Port int `mapstructure:"port"`
}
// GatewayConfig 网关配置
type GatewayConfig struct {
BaseURL string `mapstructure:"base_url"`
Timeout int `mapstructure:"timeout"`
Auth GatewayAuthConfig `mapstructure:"auth"`
}
// GatewayAuthConfig 网关鉴权配置
type GatewayAuthConfig struct {
LoginURL string `mapstructure:"login_url"`
Key string `mapstructure:"key"`
}
// SSOConfig SSO配置
type SSOConfig struct {
SSOServerURL string `mapstructure:"sso_server_url"`
ClientID string `mapstructure:"client_id"`
ClientSecret string `mapstructure:"client_secret"`
RedirectURI string `mapstructure:"redirect_uri"`
Scope string `mapstructure:"scope"`
ResourceAud string `mapstructure:"resource_aud"`
AdminToken string `mapstructure:"admin_token"`
Timeout time.Duration `mapstructure:"timeout"`
}
// MessagePushConfig 消息推送配置
type MessagePushConfig struct {
GoalfyMaxBaseURL string `mapstructure:"goalfymax_base_url"`
Timeout int `mapstructure:"timeout"`
RetryCount int `mapstructure:"retry_count"`
RetryInterval int `mapstructure:"retry_interval"`
}
// AlertConfig 告警配置
type AlertConfig struct {
DingTalk DingTalkConfig `mapstructure:"dingtalk"`
}
// DingTalkConfig 钉钉配置
type DingTalkConfig struct {
Enabled bool `mapstructure:"enabled"`
Webhook string `mapstructure:"webhook"`
Secret string `mapstructure:"secret"`
TimeoutSeconds int `mapstructure:"timeout_seconds"`
Keyword string `mapstructure:"keyword"`
}
// PayConfig 支付服务配置
type PayConfig struct {
BaseURL string `mapstructure:"base_url"`
Timeout int `mapstructure:"timeout"`
}
// OssConfig 对象存储S3兼容配置
type OssConfig struct {
Endpoint string `mapstructure:"endpoint"`
Region string `mapstructure:"region"`
AccessKeyID string `mapstructure:"access_key_id"`
AccessKeySecret string `mapstructure:"access_key_secret"`
Bucket string `mapstructure:"bucket"`
AssumeRoleArn string `mapstructure:"assume_role_arn"`
PresignUrlExpire time.Duration `mapstructure:"presign_url_expire"`
}
// RedisConfig Redis配置
type RedisConfig struct {
Addr string `mapstructure:"addr"`
Password string `mapstructure:"password"`
DB int `mapstructure:"db"`
}
// EmailConfig 邮件配置
type EmailConfig struct {
Sender string `mapstructure:"sender"`
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
InviteURLPrefix string `mapstructure:"invite_url_prefix"`
}
// Config 定义总配置结构
type Config struct {
Server ServerConfig `mapstructure:"server"`
Database DatabaseConfig `mapstructure:"database"`
Gateway GatewayConfig `mapstructure:"gateway"`
SSO SSOConfig `mapstructure:"sso"`
MessagePush MessagePushConfig `mapstructure:"message_push"`
Alert AlertConfig `mapstructure:"alert"`
Oss OssConfig `mapstructure:"oss"`
Redis RedisConfig `mapstructure:"redis"`
Email EmailConfig `mapstructure:"email"`
Log LogConfig `mapstructure:"log"`
Client ClientConfig `mapstructure:"client"`
PostgreSQL PostgreSQLConfig `mapstructure:"postgresql"`
Pay PayConfig `mapstructure:"pay"`
Jobs JobsConfig `mapstructure:"jobs"`
}
// LogConfig 日志配置
type LogConfig struct {
Level string `mapstructure:"level"`
Format string `mapstructure:"format"`
Output string `mapstructure:"output"`
}
// ClientOption 客户端选项
type ClientOption struct {
Key string `mapstructure:"key" json:"key"`
Value string `mapstructure:"value" json:"value"`
Label string `json:"label"` // 用于前端展示格式key+value
}
// ClientConfig 客户端配置
type ClientConfig struct {
Options []ClientOption `mapstructure:"options"`
}
// PostgreSQLConfig PG配置
type PostgreSQLConfig struct {
DSN string `mapstructure:"dsn"`
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
DBName string `mapstructure:"dbname"`
SSLMode string `mapstructure:"sslmode"`
MaxOpenConns int `mapstructure:"max_open_conns"`
MaxIdleConns int `mapstructure:"max_idle_conns"`
ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"`
ConnMaxIdleTime time.Duration `mapstructure:"conn_max_idle_time"`
}
type JobsConfig struct {
McpUsageBalance McpUsageBalanceJobConfig `mapstructure:"mcp_usage_balance"`
ModelTokenBalance ModelTokenBalanceJobConfig `mapstructure:"model_token_balance"`
}
type McpUsageBalanceJobConfig struct {
Enabled bool `mapstructure:"enabled"`
RunOnStartup bool `mapstructure:"run_on_startup"`
DelayMinutes int `mapstructure:"delay_minutes"`
}
type ModelTokenBalanceJobConfig struct {
Enabled bool `mapstructure:"enabled"`
RunOnStartup bool `mapstructure:"run_on_startup"`
DelayMinutes int `mapstructure:"delay_minutes"`
}
func (j JobsConfig) IsMcpUsageEnabled() bool {
return j.McpUsageBalance.Enabled
}
var (
config *Config
configLock sync.RWMutex
)
// LoadConfig 从文件加载配置
func LoadConfig(path string) error {
fmt.Printf("Loading config from %s\n", path)
viper.SetConfigFile(path)
err := viper.ReadInConfig()
if err != nil {
return fmt.Errorf("读取配置文件失败: %w", err)
}
if err := viper.Unmarshal(&config); err != nil {
return fmt.Errorf("解析配置文件失败: %w", err)
}
// 设置默认值
if config.Server.Addr == "" {
config.Server.Addr = "0.0.0.0"
}
if config.Server.Port == 0 {
config.Server.Port = 8080
}
if config.Database.MaxIdleConns == 0 {
config.Database.MaxIdleConns = 10
}
if config.Database.MaxOpenConns == 0 {
config.Database.MaxOpenConns = 100
}
if config.Database.LogLevel == "" {
config.Database.LogLevel = "info"
}
if config.Log.Level == "" {
config.Log.Level = "info"
}
if config.Log.Format == "" {
config.Log.Format = "json"
}
if config.Log.Output == "" {
config.Log.Output = "stdout"
}
if config.Jobs.McpUsageBalance.DelayMinutes <= 0 {
config.Jobs.McpUsageBalance.DelayMinutes = 5
}
if config.Jobs.ModelTokenBalance.DelayMinutes <= 0 {
config.Jobs.ModelTokenBalance.DelayMinutes = 5
}
if config.Gateway.BaseURL == "" {
config.Gateway.BaseURL = "http://localhost:8080"
}
if config.Gateway.Timeout == 0 {
config.Gateway.Timeout = 30
}
if config.Gateway.Auth.LoginURL == "" {
config.Gateway.Auth.LoginURL = "http://44.247.156.94:8080/aigateway-admin/api/login"
}
if config.Gateway.Auth.Key == "" {
config.Gateway.Auth.Key = "Jiahe.123"
}
if config.MessagePush.GoalfyMaxBaseURL == "" {
config.MessagePush.GoalfyMaxBaseURL = "http://goalfymax-backend:8080"
}
if config.MessagePush.Timeout == 0 {
config.MessagePush.Timeout = 30
}
if config.MessagePush.RetryCount == 0 {
config.MessagePush.RetryCount = 3
}
if config.MessagePush.RetryInterval == 0 {
config.MessagePush.RetryInterval = 1000
}
if config.Pay.BaseURL == "" {
config.Pay.BaseURL = "http://goalfy-pay:8080"
}
if config.Pay.Timeout == 0 {
config.Pay.Timeout = 30
}
if config.Redis.Addr == "" {
config.Redis.Addr = "localhost:6379"
}
if config.Redis.DB == 0 {
config.Redis.DB = 0
}
// OSS 预签名默认过期时间
if config.Oss.PresignUrlExpire == 0 {
config.Oss.PresignUrlExpire = 10 * time.Minute
}
if config.Alert.DingTalk.TimeoutSeconds <= 0 {
config.Alert.DingTalk.TimeoutSeconds = 5
}
// 设置客户端默认选项
if len(config.Client.Options) == 0 {
config.Client.Options = []ClientOption{
{Key: "5hNXkkkVPfFWUjRvzVP23w", Value: "https://ob-staging-goalfymax.goalfyai.com/"},
{Key: "J10f8yxU1XDl1Tn00MXKeA", Value: "https://staging-goalfymax.goalfyai.com/"},
{Key: "xRpT9mgNpt2YvoY9z4FToA", Value: "https://goalfymax.goalfyai.com/"},
}
}
// 为每个客户端选项生成 label
for i := range config.Client.Options {
config.Client.Options[i].Label = config.Client.Options[i].Key + "+" + config.Client.Options[i].Value
}
return nil
}
// GetConfig 获取当前配置
func GetConfig() *Config {
configLock.RLock()
defer configLock.RUnlock()
return config
}
// GetDatabaseConfig 获取数据库配置
func GetDatabaseConfig() mysql.Config {
cfg := GetConfig()
return mysql.Config{
DSN: cfg.Database.DSN,
}
}
// GetClientOptions 获取客户端选项列表
func GetClientOptions() []ClientOption {
cfg := GetConfig()
return cfg.Client.Options
}
// GetClientValue 根据key获取客户端的展示值
func GetClientValue(key string) string {
options := GetClientOptions()
for _, opt := range options {
if opt.Key == key {
return opt.Value
}
}
return ""
}