// Package config 使用仓库统一的 BSM 配置加载与校验方式。 package config import ( "net" "net/url" "strings" "git.apinb.com/bsm-sdk/core/conf" ) // Spec 是 Platform API 的运行配置。 var Spec SrvConfig // GlobalConfig 保存多个管理端共用的运行参数。 type GlobalConfig struct { UserRegisterURL string `yaml:"UserRegisterURL"` ManualRechargeMaxAmount int64 `yaml:"ManualRechargeMaxAmount"` MockVerificationEnabled bool `yaml:"MockVerificationEnabled"` MockVerificationCode string `yaml:"MockVerificationCode"` VerificationTTLSeconds int `yaml:"VerificationTTLSeconds"` VerificationSendIntervalSeconds int `yaml:"VerificationSendIntervalSeconds"` MockPaymentEnabled bool `yaml:"MockPaymentEnabled"` DeliveryArrivalRadiusMeters float64 `yaml:"DeliveryArrivalRadiusMeters"` UploadVideoMaxSize int64 `yaml:"UploadVideoMaxSize"` FieldEncryptionKey string `yaml:"FieldEncryptionKey"` } // WalletConfig 是平台既有钱包逻辑的内部兼容视图,值由 Global 注入。 type WalletConfig struct { ManualRechargeMaxAmount int64 `yaml:"-"` } // AlipayConfig 保存支付宝证书模式商户配置。私钥和证书只允许服务端读取。 type AlipayConfig struct { Enabled bool `yaml:"Enabled"` Production bool `yaml:"Production"` AppID string `yaml:"AppID"` PrivateKeyPath string `yaml:"PrivateKeyPath"` AppPublicCertPath string `yaml:"AppPublicCertPath"` AlipayPublicCertPath string `yaml:"AlipayPublicCertPath"` AlipayRootCertPath string `yaml:"AlipayRootCertPath"` NotifyURL string `yaml:"NotifyURL"` ReturnURL string `yaml:"ReturnURL"` } // WechatPayConfig 保存微信支付 API v3 平台单商户配置。 type WechatPayConfig struct { Enabled bool `yaml:"Enabled"` MerchantID string `yaml:"MerchantID"` MerchantCertificateSerial string `yaml:"MerchantCertificateSerial"` MerchantPrivateKeyPath string `yaml:"MerchantPrivateKeyPath"` APIv3Key string `yaml:"APIv3Key"` AppAppID string `yaml:"AppAppID"` OfficialAccountAppID string `yaml:"OfficialAccountAppID"` MiniProgramAppID string `yaml:"MiniProgramAppID"` NotifyURL string `yaml:"NotifyURL"` } // PaymentConfig 保存统一支付、退款和 Worker 内部调用参数。 type PaymentConfig struct { ExpireMinutes int `yaml:"ExpireMinutes"` RefundWindowDays int `yaml:"RefundWindowDays"` InternalServiceToken string `yaml:"InternalServiceToken"` Alipay AlipayConfig `yaml:"Alipay"` Wechat WechatPayConfig `yaml:"Wechat"` } // IoTConfig 保存 API 与 IoT 链路间的内部认证配置。 type IoTConfig struct { InternalServiceToken string `yaml:"InternalServiceToken"` } // SrvConfig 与仓库现有进程的配置结构保持一致。 type SrvConfig struct { conf.Base `yaml:",inline"` Databases *conf.DBConf `yaml:"Databases"` Rpc map[string]conf.RpcConf `yaml:"Rpc"` Apm *conf.ApmConf `yaml:"APM"` Global GlobalConfig `yaml:"Global"` Wallet WalletConfig `yaml:"-"` Payment PaymentConfig `yaml:"Payment"` IoT IoTConfig `yaml:"IoT"` } // New 初始化 BSM 配置并校验服务监听地址。 func New(srvKey string) { conf.New(srvKey, &Spec) Spec.Port = conf.CheckPort(Spec.Port) Spec.BindIP = conf.CheckIP(Spec.BindIP) Spec.Addr = net.JoinHostPort(Spec.BindIP, Spec.Port) conf.NotNil(Spec.Service, Spec.Cache) if Spec.Databases == nil || len(Spec.Databases.Source) == 0 { panic("Databases configuration is required") } if Spec.Global.ManualRechargeMaxAmount <= 0 { panic("Global.ManualRechargeMaxAmount must be greater than zero") } if Spec.Global.VerificationTTLSeconds <= 0 || Spec.Global.VerificationSendIntervalSeconds <= 0 { panic("Global verification timeouts must be greater than zero") } if Spec.Global.MockVerificationEnabled && len(Spec.Global.MockVerificationCode) < 4 { panic("Global.MockVerificationCode must contain at least four characters") } if Spec.Global.DeliveryArrivalRadiusMeters <= 0 || Spec.Global.UploadVideoMaxSize <= 0 { panic("Global delivery radius and upload video size must be greater than zero") } if len(strings.TrimSpace(Spec.Global.FieldEncryptionKey)) < 32 { panic("Global.FieldEncryptionKey must contain at least 32 characters") } Spec.Wallet.ManualRechargeMaxAmount = Spec.Global.ManualRechargeMaxAmount if Spec.Payment.ExpireMinutes <= 0 || Spec.Payment.RefundWindowDays <= 0 { panic("Payment expiration and refund window must be greater than zero") } if len(strings.TrimSpace(Spec.IoT.InternalServiceToken)) < 16 { panic("IoT internal token is required") } registerURL, err := url.ParseRequestURI(Spec.Global.UserRegisterURL) if err != nil || (registerURL.Scheme != "http" && registerURL.Scheme != "https") || registerURL.Host == "" { panic("Global.UserRegisterURL must be a valid HTTP or HTTPS URL") } conf.PrintInfo(Spec.Addr) }