Files
platform/pkg/env/env.go

215 lines
7.3 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 env
import (
"fmt"
"log/slog"
"os"
"platform/pkg/u"
"slices"
"strconv"
"github.com/gofiber/fiber/v2/log"
"github.com/joho/godotenv"
)
const (
RunModeDev = "development"
RunModeProd = "production"
)
var (
RunMode = RunModeProd
LogLevel = slog.LevelDebug
TradeExpire = 15 * 60 // 交易过期时间,单位秒。默认 900 秒15 分钟)
SessionAccessExpire = 60 * 60 * 2 // 访问令牌过期时间,单位秒。默认 2 小时
SessionRefreshExpire = 60 * 60 * 24 * 7 // 刷新令牌过期时间,单位秒。默认 7 天
DebugHttpDump = false // 是否打印请求和响应的原始数据
DebugExternalChange = true // 是否实际执行外部非幂等接口调用,在开发调试时可以关闭,避免对外部数据产生影响
DbHost = "localhost"
DbPort = "5432"
DbName string
DbUserName string
DbPassword string
RedisHost = "localhost"
RedisPort = "6379"
RedisPassword = ""
OtelHost string
OtelPort string
BaiyinCloudUrl string
BaiyinTokenUrl string
IdenCallbackUrl string
IdenAccessKey string
IdenSecretKey string
AlipayAppId string
AlipayAppPrivateKey string
AlipayPublicKey string
AlipayApiCert string
AlipayProduction = false
WechatPayAppId string
WechatPayMchId string
WechatPayMchPrivateKeySerial string
WechatPayMchPrivateKey string
WechatPayPublicKeyId string
WechatPayPublicKey string
WechatPayApiCert string
WechatPayCallbackUrl string
AliyunAccessKey string
AliyunAccessKeySecret string
AliyunSmsSignature string
AliyunSmsTemplateLogin string
SftPayEnable = false
SftPayAppId string
SftPayRouteId string
SftPayAppPrivateKey string
SftPayPublicKey string
SftReturnUrl string
SftNotifyUrl string
)
func Init() {
err := godotenv.Load()
if err != nil {
log.Debug("❓ 没有本地环境变量")
} else {
log.Debug("✔ 加载本地环境变量")
}
// 收集所有错误
var errs []error
errs = append(errs, parse(&RunMode, "RUN_MODE", true, &[]string{RunModeDev, RunModeProd}))
errs = append(errs, parse(&LogLevel, "LOG_LEVEL", true, nil, func(value string) (slog.Level, error) {
switch value {
case "debug":
return slog.LevelDebug, nil
case "info":
return slog.LevelInfo, nil
case "warn":
return slog.LevelWarn, nil
case "error":
return slog.LevelError, nil
default:
return slog.LevelInfo, fmt.Errorf("无效的日志级别: %s", value)
}
}))
errs = append(errs, parse(&TradeExpire, "TRADE_EXPIRE", true, nil))
errs = append(errs, parse(&SessionAccessExpire, "SESSION_ACCESS_EXPIRE", true, nil))
errs = append(errs, parse(&SessionRefreshExpire, "SESSION_REFRESH_EXPIRE", true, nil))
errs = append(errs, parse(&DebugHttpDump, "DEBUG_HTTP_DUMP", true, nil))
errs = append(errs, parse(&DebugExternalChange, "DEBUG_EXTERNAL_CHANGE", true, nil))
errs = append(errs, parse(&DbHost, "DB_HOST", true, nil))
errs = append(errs, parse(&DbPort, "DB_PORT", true, nil))
errs = append(errs, parse(&DbName, "DB_NAME", false, nil))
errs = append(errs, parse(&DbUserName, "DB_USERNAME", false, nil))
errs = append(errs, parse(&DbPassword, "DB_PASSWORD", false, nil))
errs = append(errs, parse(&RedisHost, "REDIS_HOST", true, nil))
errs = append(errs, parse(&RedisPort, "REDIS_PORT", true, nil))
errs = append(errs, parse(&RedisPassword, "REDIS_PASS", true, nil))
errs = append(errs, parse(&OtelHost, "OTEL_HOST", true, nil))
errs = append(errs, parse(&OtelPort, "OTEL_PORT", true, nil))
errs = append(errs, parse(&BaiyinCloudUrl, "BAIYIN_CLOUD_URL", false, nil))
errs = append(errs, parse(&BaiyinTokenUrl, "BAIYIN_TOKEN_URL", false, nil))
errs = append(errs, parse(&IdenCallbackUrl, "IDEN_CALLBACK_URL", false, nil))
errs = append(errs, parse(&IdenAccessKey, "IDEN_ACCESS_KEY", false, nil))
errs = append(errs, parse(&IdenSecretKey, "IDEN_SECRET_KEY", false, nil))
errs = append(errs, parse(&AlipayAppId, "ALIPAY_APP_ID", false, nil))
errs = append(errs, parse(&AlipayAppPrivateKey, "ALIPAY_APP_PRIVATE_KEY", false, nil))
errs = append(errs, parse(&AlipayPublicKey, "ALIPAY_PUBLIC_KEY", false, nil))
errs = append(errs, parse(&AlipayApiCert, "ALIPAY_API_CERT", false, nil))
errs = append(errs, parse(&AlipayProduction, "ALIPAY_PRODUCTION", true, nil))
errs = append(errs, parse(&WechatPayAppId, "WECHATPAY_APP_ID", false, nil))
errs = append(errs, parse(&WechatPayMchId, "WECHATPAY_MCH_ID", false, nil))
errs = append(errs, parse(&WechatPayMchPrivateKeySerial, "WECHATPAY_MCH_PRIVATE_KEY_SERIAL", false, nil))
errs = append(errs, parse(&WechatPayMchPrivateKey, "WECHATPAY_MCH_PRIVATE_KEY", false, nil))
errs = append(errs, parse(&WechatPayPublicKeyId, "WECHATPAY_PUBLIC_KEY_ID", false, nil))
errs = append(errs, parse(&WechatPayPublicKey, "WECHATPAY_PUBLIC_KEY", false, nil))
errs = append(errs, parse(&WechatPayApiCert, "WECHATPAY_API_CERT", false, nil))
errs = append(errs, parse(&WechatPayCallbackUrl, "WECHATPAY_CALLBACK_URL", false, nil))
errs = append(errs, parse(&AliyunAccessKey, "ALIYUN_ACCESS_KEY", false, nil))
errs = append(errs, parse(&AliyunAccessKeySecret, "ALIYUN_ACCESS_KEY_SECRET", false, nil))
errs = append(errs, parse(&AliyunSmsSignature, "ALIYUN_SMS_SIGNATURE", false, nil))
errs = append(errs, parse(&AliyunSmsTemplateLogin, "ALIYUN_SMS_TEMPLATE_LOGIN", false, nil))
errs = append(errs, parse(&SftPayEnable, "SFTPAY_ENABLE", true, nil))
errs = append(errs, parse(&SftPayAppId, "SFTPAY_APP_ID", false, nil))
errs = append(errs, parse(&SftPayRouteId, "SFTPAY_ROUTE_ID", true, nil))
errs = append(errs, parse(&SftPayAppPrivateKey, "SFTPAY_APP_PRIVATE_KEY", false, nil))
errs = append(errs, parse(&SftPayPublicKey, "SFTPAY_PUBLIC_KEY", false, nil))
errs = append(errs, parse(&SftReturnUrl, "SFTPAY_RETURN_URL", true, nil))
errs = append(errs, parse(&SftNotifyUrl, "SFTPAY_NOTIFY_URL", true, nil))
// 统一处理错误
if err := u.CombineErrors(errs); err != nil {
panic(err)
}
}
func parse[T comparable](ptr *T, key string, inited bool, enum *[]string, convOpt ...func(value string) (T, error)) error {
value := os.Getenv(key)
// 处理空值
if value == "" {
if inited {
return nil
} else {
return fmt.Errorf("环境变量 %s 未设置", key)
}
}
// 处理枚举映射
if enum != nil {
valid := slices.Contains(*enum, value)
if !valid {
return fmt.Errorf("环境变量 %s 的值 '%s' 必须是 %v 之一", key, value, *enum)
}
}
// 根据指针类型进行赋值和类型转换
switch p := any(ptr).(type) {
case *string:
*p = value
case *int:
intValue, err := strconv.Atoi(value)
if err != nil {
return fmt.Errorf("环境变量 %s 的值 '%s' 不是有效的整数: %v", key, value, err)
}
*p = intValue
case *bool:
boolValue, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("环境变量 %s 的值 '%s' 不是有效的布尔值: %v", key, value, err)
}
*p = boolValue
default:
if len(convOpt) == 0 {
return fmt.Errorf("环境变量 %s 的值 '%s' 无法赋值到目标类型", key, value)
}
conv := convOpt[0]
convertedValue, err := conv(value)
if err != nil {
return fmt.Errorf("环境变量 %s 的值 '%s' 转换失败: %v", key, value, err)
}
*ptr = convertedValue
}
return nil
}