log: add logrus log lib and add telegram notification push as an option

This commit is contained in:
tangmengqiu
2025-11-04 23:48:32 -05:00
parent bb6a1ac073
commit 9658785c6b
10 changed files with 648 additions and 21 deletions
+4 -1
View File
@@ -20,5 +20,8 @@
"max_daily_loss": 10.0,
"max_drawdown": 20.0,
"stop_trading_minutes": 60,
"jwt_secret": "Qk0kAa+d0iIEzXVHXbNbm+UaN3RNabmWtH8rDWZ5OPf+4GX8pBflAHodfpbipVMyrw1fsDanHsNBjhgbDeK9Jg=="
"jwt_secret": "Qk0kAa+d0iIEzXVHXbNbm+UaN3RNabmWtH8rDWZ5OPf+4GX8pBflAHodfpbipVMyrw1fsDanHsNBjhgbDeK9Jg==",
"log": {
"level": "info"
}
}
+15
View File
@@ -50,6 +50,20 @@ type LeverageConfig struct {
AltcoinLeverage int `json:"altcoin_leverage"` // 山寨币的杠杆倍数(主账户建议5-20,子账户≤5)
}
// LogConfig 日志配置
type LogConfig struct {
Level string `json:"level"` // 日志级别: debug, info, warn, error (默认: info)
Telegram *TelegramConfig `json:"telegram"` // Telegram推送配置(可选)
}
// TelegramConfig Telegram推送配置(简化版,只保留必需字段)
type TelegramConfig struct {
Enabled bool `json:"enabled"` // 是否启用(默认: false
BotToken string `json:"bot_token"` // Bot Token
ChatID int64 `json:"chat_id"` // Chat ID
MinLevel string `json:"min_level"` // 最低日志级别,该级别及以上的日志会推送到Telegram(可选,默认: error
}
// Config 总配置
type Config struct {
Traders []TraderConfig `json:"traders"`
@@ -60,6 +74,7 @@ type Config struct {
MaxDrawdown float64 `json:"max_drawdown"`
StopTradingMinutes int `json:"stop_trading_minutes"`
Leverage LeverageConfig `json:"leverage"` // 杠杆配置
Log *LogConfig `json:"log"` // 日志配置(可选)
}
// LoadConfig 从文件加载配置
+2
View File
@@ -6,11 +6,13 @@ require (
github.com/adshao/go-binance/v2 v2.8.7
github.com/ethereum/go-ethereum v1.16.5
github.com/gin-gonic/gin v1.11.0
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/mattn/go-sqlite3 v1.14.16
github.com/pquerna/otp v1.4.0
github.com/sirupsen/logrus v1.9.3
github.com/sonirico/go-hyperliquid v0.17.0
golang.org/x/crypto v0.42.0
)
+6
View File
@@ -62,6 +62,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
@@ -155,6 +157,8 @@ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sonirico/go-hyperliquid v0.17.0 h1:eXYACWupwu41O1VtKw17dqe9oOLQ1A2nRElGhg5Ox+4=
github.com/sonirico/go-hyperliquid v0.17.0/go.mod h1:sH51Vsu+tPUwc95TL2MoQ8YXSewLWBEJirgzo7sZx6w=
github.com/sonirico/vago v0.9.0 h1:DF2OWW2Aaf1xPZmnFv79kBrHmjKX3mVvMbP08vERlKo=
@@ -167,6 +171,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
@@ -206,6 +211,7 @@ golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+64
View File
@@ -0,0 +1,64 @@
package logger
import (
"github.com/sirupsen/logrus"
)
// Config 日志配置(简化版)
type Config struct {
Level string `json:"level"` // 日志级别: debug, info, warn, error (默认: info)
Telegram *TelegramConfig `json:"telegram"` // Telegram推送配置(可选)
}
// TelegramConfig Telegram推送配置(简化版,高级参数使用默认值)
type TelegramConfig struct {
Enabled bool `json:"enabled"` // 是否启用(默认: false
BotToken string `json:"bot_token"` // Bot Token
ChatID int64 `json:"chat_id"` // Chat ID
MinLevel string `json:"min_level"` // 最低日志级别,该级别及以上的日志会推送到Telegram(可选,默认: error
}
// SetDefaults 设置默认值
func (c *Config) SetDefaults() {
if c.Level == "" {
c.Level = "info"
}
}
// GetLogrusLevels 返回要推送到Telegram的日志级别
// 根据配置的MinLevel返回该级别及以上的所有日志级别
// 如果未配置或配置无效,默认返回error, fatal, panic(向后兼容)
func (tc *TelegramConfig) GetLogrusLevels() []logrus.Level {
// 如果未配置,使用默认值error(向后兼容)
minLevelStr := tc.MinLevel
if minLevelStr == "" {
minLevelStr = "error"
}
// 解析配置的日志级别
minLevel, err := logrus.ParseLevel(minLevelStr)
if err != nil {
// 如果解析失败,使用默认值error(向后兼容)
minLevel = logrus.ErrorLevel
}
// 定义所有日志级别(从高到低:panic, fatal, error, warn, info, debug
allLevels := []logrus.Level{
logrus.PanicLevel,
logrus.FatalLevel,
logrus.ErrorLevel,
logrus.WarnLevel,
logrus.InfoLevel,
logrus.DebugLevel,
}
// 返回所有大于等于minLevel的日志级别
var result []logrus.Level
for _, level := range allLevels {
if level <= minLevel {
result = append(result, level)
}
}
return result
}
+33
View File
@@ -0,0 +1,33 @@
{
"traders": [
{
"id": "trader1",
"name": "AI Trader 1",
"enabled": true,
"ai_model": "deepseek",
"exchange": "binance",
"binance_api_key": "your_api_key",
"binance_secret_key": "your_secret_key",
"deepseek_key": "your_deepseek_key",
"initial_balance": 1000,
"scan_interval_minutes": 3
}
],
"use_default_coins": true,
"default_coins": ["BTCUSDT", "ETHUSDT", "SOLUSDT"],
"api_server_port": 8080,
"leverage": {
"btc_eth_leverage": 5,
"altcoin_leverage": 5
},
"log": {
"level": "info",
"telegram": {
"enabled": true,
"bot_token": "79472419:feafe231414",
"chat_id": -100323252626,
"min_level": "error"
}
},
"_comment": "日志配置说明:level 可选值为 debug/info/warn/error,默认 info。telegram 部分作为可选配置, Telegram 推送默认为 error/fatal/panic 级别,min_level 如果设置为warn,则推送warn级别及以上的日志"
}
+210
View File
@@ -0,0 +1,210 @@
package logger
import (
"nofx/config"
"os"
"github.com/sirupsen/logrus"
)
var (
// Log 全局logger实例
Log *logrus.Logger
// telegramHook 保存hook引用,用于优雅关闭
telegramHook *TelegramHook
)
// ============================================================================
// 初始化函数
// ============================================================================
// Init 初始化全局logger
// 如果config为nil,使用默认配置(console输出,info级别)
func Init(cfg *Config) error {
Log = logrus.New()
// 如果没有配置,使用默认值
if cfg == nil {
cfg = &Config{Level: "info"}
}
// 设置默认值
cfg.SetDefaults()
// 设置日志级别
level, err := logrus.ParseLevel(cfg.Level)
if err != nil {
level = logrus.InfoLevel
}
Log.SetLevel(level)
// 设置格式化器(固定使用彩色文本格式)
Log.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
TimestampFormat: "2006-01-02 15:04:05",
ForceColors: true,
})
// 设置输出目标(默认stdout
Log.SetOutput(os.Stdout)
// 启用调用位置信息
Log.SetReportCaller(true)
// 添加Telegram Hook(可选)
if cfg.Telegram != nil && cfg.Telegram.Enabled {
if err := setupTelegramHook(cfg.Telegram); err != nil {
Log.Warnf("初始化Telegram推送失败,将继续使用普通日志: %v", err)
}
}
return nil
}
// setupTelegramHook 设置Telegram Hook
func setupTelegramHook(telegramCfg *TelegramConfig) error {
hook, err := NewTelegramHook(telegramCfg)
if err != nil {
return err
}
Log.AddHook(hook)
telegramHook = hook
Log.Info("✅ Telegram日志推送已启用")
return nil
}
// InitWithSimpleConfig 使用简化配置初始化logger
// 适用于只需要基本功能的场景
func InitWithSimpleConfig(level string) error {
return Init(&Config{Level: level})
}
// InitWithTelegram 使用Telegram配置初始化logger
func InitWithTelegram(botToken string, chatID int64) error {
return Init(&Config{
Level: "info",
Telegram: &TelegramConfig{
Enabled: true,
BotToken: botToken,
ChatID: chatID,
},
})
}
// InitFromLogConfig 从config.LogConfig初始化logger
func InitFromLogConfig(logConfig *config.LogConfig) error {
if logConfig == nil {
return InitWithSimpleConfig("info")
}
cfg := &Config{
Level: logConfig.Level,
}
if cfg.Level == "" {
cfg.Level = "info"
}
// 如果启用了Telegram,添加配置
if logConfig.Telegram != nil && logConfig.Telegram.Enabled {
if botToken := logConfig.Telegram.BotToken; botToken != "" && logConfig.Telegram.ChatID != 0 {
cfg.Telegram = &TelegramConfig{
Enabled: true,
BotToken: botToken,
ChatID: logConfig.Telegram.ChatID,
MinLevel: logConfig.Telegram.MinLevel,
}
}
}
return Init(cfg)
}
// InitFromParams 从参数初始化logger
// 适用于不依赖config包的场景
func InitFromParams(level string, telegramEnabled bool, botToken string, chatID int64) error {
cfg := &Config{Level: level}
if telegramEnabled && botToken != "" && chatID != 0 {
cfg.Telegram = &TelegramConfig{
Enabled: true,
BotToken: botToken,
ChatID: chatID,
}
}
return Init(cfg)
}
// Shutdown 优雅关闭logger(主要用于关闭Telegram发送器)
func Shutdown() {
if telegramHook != nil {
telegramHook.Stop()
telegramHook = nil
}
}
// ============================================================================
// 日志记录函数
// ============================================================================
// WithFields 创建带字段的logger entry
func WithFields(fields logrus.Fields) *logrus.Entry {
return Log.WithFields(fields)
}
// WithField 创建带单个字段的logger entry
func WithField(key string, value interface{}) *logrus.Entry {
return Log.WithField(key, value)
}
// add debug, info, warn
func Debug(args ...interface{}) {
Log.Debug(args...)
}
func Info(args ...interface{}) {
Log.Info(args...)
}
func Warn(args ...interface{}) {
Log.Warn(args...)
}
func Debugf(format string, args ...interface{}) {
Log.Debugf(format, args...)
}
func Infof(format string, args ...interface{}) {
Log.Infof(format, args...)
}
func Warnf(format string, args ...interface{}) {
Log.Warnf(format, args...)
}
func Error(args ...interface{}) {
Log.Error(args...)
}
func Errorf(format string, args ...interface{}) {
Log.Errorf(format, args...)
}
func Fatal(args ...interface{}) {
Log.Fatal(args...)
}
func Fatalf(format string, args ...interface{}) {
Log.Fatalf(format, args...)
}
func Panic(args ...interface{}) {
Log.Panic(args...)
}
func Panicf(format string, args ...interface{}) {
Log.Panicf(format, args...)
}
+158
View File
@@ -0,0 +1,158 @@
package logger
import (
"fmt"
"runtime"
"strings"
"github.com/sirupsen/logrus"
)
// TelegramHook 实现logrus.Hook接口,将日志推送到Telegram
type TelegramHook struct {
sender *TelegramSender
levels []logrus.Level
enabled bool
}
// NewTelegramHook 创建Telegram Hook
func NewTelegramHook(config *TelegramConfig) (*TelegramHook, error) {
if !config.Enabled {
return &TelegramHook{enabled: false}, nil
}
if config.BotToken == "" || config.ChatID == 0 {
return nil, fmt.Errorf("telegram配置不完整: bot_token和chat_id不能为空")
}
// 创建发送器(使用默认参数)
sender, err := NewTelegramSender(config.BotToken, config.ChatID)
if err != nil {
return nil, fmt.Errorf("创建telegram发送器失败: %w", err)
}
hook := &TelegramHook{
sender: sender,
levels: config.GetLogrusLevels(),
enabled: true,
}
return hook, nil
}
// Levels 返回需要触发的日志级别
func (h *TelegramHook) Levels() []logrus.Level {
if !h.enabled {
return []logrus.Level{}
}
return h.levels
}
// Fire 当日志触发时调用
func (h *TelegramHook) Fire(entry *logrus.Entry) error {
if !h.enabled {
return nil
}
// 格式化消息
message := h.formatMessage(entry)
// 异步发送(非阻塞)
h.sender.SendAsync(message)
return nil
}
// formatMessage 格式化日志消息为Telegram格式
func (h *TelegramHook) formatMessage(entry *logrus.Entry) string {
// 级别emoji
levelEmoji := h.getLevelEmoji(entry.Level)
// 基本信息
var builder strings.Builder
builder.WriteString(fmt.Sprintf("%s *%s*: 系统日志警报\n", levelEmoji, strings.ToUpper(entry.Level.String())))
builder.WriteString(fmt.Sprintf("📝 消息: `%s`\n", escapeMarkdown(entry.Message)))
// 字段信息
if len(entry.Data) > 0 {
builder.WriteString("📊 字段:\n")
for key, value := range entry.Data {
builder.WriteString(fmt.Sprintf(" • %s: `%v`\n", key, value))
}
}
// 调用位置
if entry.HasCaller() {
file := entry.Caller.File
// 只保留相对路径
if idx := strings.Index(file, "nofx/"); idx >= 0 {
file = file[idx:]
}
builder.WriteString(fmt.Sprintf("📍 位置: `%s:%d`\n", file, entry.Caller.Line))
} else {
// 如果entry没有caller,手动获取
if _, file, line, ok := runtime.Caller(8); ok {
if idx := strings.Index(file, "nofx/"); idx >= 0 {
file = file[idx:]
}
builder.WriteString(fmt.Sprintf("📍 位置: `%s:%d`\n", file, line))
}
}
// 时间戳
builder.WriteString(fmt.Sprintf("🕐 时间: `%s`", entry.Time.Format("2006-01-02 15:04:05")))
return builder.String()
}
// getLevelEmoji 获取日志级别对应的emoji
func (h *TelegramHook) getLevelEmoji(level logrus.Level) string {
switch level {
case logrus.PanicLevel:
return "🔴"
case logrus.FatalLevel:
return "🔴"
case logrus.ErrorLevel:
return "🟠"
case logrus.WarnLevel:
return "🟡"
case logrus.InfoLevel:
return "🟢"
case logrus.DebugLevel:
return "🔵"
default:
return "⚪"
}
}
// escapeMarkdown 转义Markdown特殊字符
func escapeMarkdown(text string) string {
replacer := strings.NewReplacer(
"_", "\\_",
"*", "\\*",
"[", "\\[",
"]", "\\]",
"(", "\\(",
")", "\\)",
"~", "\\~",
"`", "\\`",
">", "\\>",
"#", "\\#",
"+", "\\+",
"-", "\\-",
"=", "\\=",
"|", "\\|",
"{", "\\{",
"}", "\\}",
".", "\\.",
"!", "\\!",
)
return replacer.Replace(text)
}
// Stop 停止Hook(优雅关闭)
func (h *TelegramHook) Stop() {
if h.enabled && h.sender != nil {
h.sender.Stop()
}
}
+120
View File
@@ -0,0 +1,120 @@
package logger
import (
"fmt"
"sync"
"time"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
// TelegramSender Telegram消息发送器(异步)
type TelegramSender struct {
bot *tgbotapi.BotAPI
chatID int64
msgChan chan string
retryCount int
retryInterval time.Duration
wg sync.WaitGroup
stopChan chan struct{}
once sync.Once
}
// NewTelegramSender 创建Telegram发送器(使用默认参数)
func NewTelegramSender(botToken string, chatID int64) (*TelegramSender, error) {
bot, err := tgbotapi.NewBotAPI(botToken)
if err != nil {
return nil, fmt.Errorf("创建telegram bot失败: %w", err)
}
// 设置为静默模式(不打印bot信息)
bot.Debug = false
sender := &TelegramSender{
bot: bot,
chatID: chatID,
msgChan: make(chan string, 20), // 固定缓冲区大小: 20
retryCount: 3, // 固定重试次数: 3
retryInterval: 3 * time.Second, // 固定重试间隔: 3秒
stopChan: make(chan struct{}),
}
// 启动异步发送协程
sender.Start()
return sender, nil
}
// Start 启动异步发送协程
func (s *TelegramSender) Start() {
s.wg.Add(1)
go s.listenAndSend()
}
// SendAsync 异步发送消息(非阻塞)
func (s *TelegramSender) SendAsync(message string) {
select {
case s.msgChan <- message:
// 成功写入缓冲区
default:
// 缓冲区满,丢弃消息(不阻塞主流程)
fmt.Printf("[Telegram] 消息缓冲区已满,消息被丢弃\n")
}
}
// listenAndSend 监听channel并发送消息
func (s *TelegramSender) listenAndSend() {
defer s.wg.Done()
for {
select {
case msg := <-s.msgChan:
s.sendWithRetry(msg)
case <-s.stopChan:
// 清空缓冲区后退出
for len(s.msgChan) > 0 {
msg := <-s.msgChan
s.sendWithRetry(msg)
}
return
}
}
}
// sendWithRetry 发送消息(带重试)
func (s *TelegramSender) sendWithRetry(message string) {
var err error
for i := 0; i < s.retryCount; i++ {
err = s.send(message)
if err == nil {
return // 发送成功
}
// 重试前等待
if i < s.retryCount-1 {
time.Sleep(s.retryInterval)
}
}
// 所有重试都失败
if err != nil {
fmt.Printf("[Telegram] 发送消息失败(已重试%d次): %v\n", s.retryCount, err)
}
}
// send 发送单条消息
func (s *TelegramSender) send(message string) error {
msg := tgbotapi.NewMessage(s.chatID, message)
msg.ParseMode = tgbotapi.ModeMarkdown
_, err := s.bot.Send(msg)
return err
}
// Stop 停止发送器(优雅关闭)
func (s *TelegramSender) Stop() {
s.once.Do(func() {
close(s.stopChan)
s.wg.Wait()
})
}
+36 -20
View File
@@ -25,39 +25,49 @@ type LeverageConfig struct {
// ConfigFile 配置文件结构,只包含需要同步到数据库的字段
type ConfigFile struct {
AdminMode bool `json:"admin_mode"`
BetaMode bool `json:"beta_mode"`
APIServerPort int `json:"api_server_port"`
UseDefaultCoins bool `json:"use_default_coins"`
DefaultCoins []string `json:"default_coins"`
CoinPoolAPIURL string `json:"coin_pool_api_url"`
OITopAPIURL string `json:"oi_top_api_url"`
MaxDailyLoss float64 `json:"max_daily_loss"`
MaxDrawdown float64 `json:"max_drawdown"`
StopTradingMinutes int `json:"stop_trading_minutes"`
Leverage LeverageConfig `json:"leverage"`
JWTSecret string `json:"jwt_secret"`
DataKLineTime string `json:"data_k_line_time"`
AdminMode bool `json:"admin_mode"`
BetaMode bool `json:"beta_mode"`
APIServerPort int `json:"api_server_port"`
UseDefaultCoins bool `json:"use_default_coins"`
DefaultCoins []string `json:"default_coins"`
CoinPoolAPIURL string `json:"coin_pool_api_url"`
OITopAPIURL string `json:"oi_top_api_url"`
MaxDailyLoss float64 `json:"max_daily_loss"`
MaxDrawdown float64 `json:"max_drawdown"`
StopTradingMinutes int `json:"stop_trading_minutes"`
Leverage LeverageConfig `json:"leverage"`
JWTSecret string `json:"jwt_secret"`
DataKLineTime string `json:"data_k_line_time"`
Log *config.LogConfig `json:"log"` // 日志配置
}
// syncConfigToDatabase 从config.json读取配置并同步到数据库
func syncConfigToDatabase(database *config.Database) error {
// loadConfigFile 读取并解析config.json文件
func loadConfigFile() (*ConfigFile, error) {
// 检查config.json是否存在
if _, err := os.Stat("config.json"); os.IsNotExist(err) {
log.Printf("📄 config.json不存在,跳过同步")
return nil
log.Printf("📄 config.json不存在,使用默认配置")
return &ConfigFile{}, nil
}
// 读取config.json
data, err := os.ReadFile("config.json")
if err != nil {
return fmt.Errorf("读取config.json失败: %w", err)
return nil, fmt.Errorf("读取config.json失败: %w", err)
}
// 解析JSON
var configFile ConfigFile
if err := json.Unmarshal(data, &configFile); err != nil {
return fmt.Errorf("解析config.json失败: %w", err)
return nil, fmt.Errorf("解析config.json失败: %w", err)
}
return &configFile, nil
}
// syncConfigToDatabase 将配置同步到数据库
func syncConfigToDatabase(database *config.Database, configFile *ConfigFile) error {
if configFile == nil {
return nil
}
log.Printf("🔄 开始同步config.json到数据库...")
@@ -156,6 +166,12 @@ func main() {
dbPath = os.Args[1]
}
// 读取配置文件
configFile, err := loadConfigFile()
if err != nil {
log.Fatalf("❌ 读取config.json失败: %v", err)
}
log.Printf("📋 初始化配置数据库: %s", dbPath)
database, err := config.NewDatabase(dbPath)
if err != nil {
@@ -164,7 +180,7 @@ func main() {
defer database.Close()
// 同步config.json到数据库
if err := syncConfigToDatabase(database); err != nil {
if err := syncConfigToDatabase(database, configFile); err != nil {
log.Printf("⚠️ 同步config.json到数据库失败: %v", err)
}