mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-10 22:36:58 +08:00
sync fork
This commit is contained in:
390
config/database.go
Normal file
390
config/database.go
Normal file
@@ -0,0 +1,390 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
// Database 配置数据库
|
||||
type Database struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewDatabase 创建配置数据库
|
||||
func NewDatabase(dbPath string) (*Database, error) {
|
||||
db, err := sql.Open("sqlite3", dbPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("打开数据库失败: %w", err)
|
||||
}
|
||||
|
||||
database := &Database{db: db}
|
||||
if err := database.createTables(); err != nil {
|
||||
return nil, fmt.Errorf("创建表失败: %w", err)
|
||||
}
|
||||
|
||||
if err := database.initDefaultData(); err != nil {
|
||||
return nil, fmt.Errorf("初始化默认数据失败: %w", err)
|
||||
}
|
||||
|
||||
return database, nil
|
||||
}
|
||||
|
||||
// createTables 创建数据库表
|
||||
func (d *Database) createTables() error {
|
||||
queries := []string{
|
||||
// AI模型配置表
|
||||
`CREATE TABLE IF NOT EXISTS ai_models (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
provider TEXT NOT NULL,
|
||||
enabled BOOLEAN DEFAULT 0,
|
||||
api_key TEXT DEFAULT '',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
|
||||
// 交易所配置表
|
||||
`CREATE TABLE IF NOT EXISTS exchanges (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL, -- 'cex' or 'dex'
|
||||
enabled BOOLEAN DEFAULT 0,
|
||||
api_key TEXT DEFAULT '',
|
||||
secret_key TEXT DEFAULT '',
|
||||
testnet BOOLEAN DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
|
||||
// 交易员配置表
|
||||
`CREATE TABLE IF NOT EXISTS traders (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
ai_model_id TEXT NOT NULL,
|
||||
exchange_id TEXT NOT NULL,
|
||||
initial_balance REAL NOT NULL,
|
||||
scan_interval_minutes INTEGER DEFAULT 3,
|
||||
is_running BOOLEAN DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (ai_model_id) REFERENCES ai_models(id),
|
||||
FOREIGN KEY (exchange_id) REFERENCES exchanges(id)
|
||||
)`,
|
||||
|
||||
// 系统配置表
|
||||
`CREATE TABLE IF NOT EXISTS system_config (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
|
||||
// 触发器:自动更新 updated_at
|
||||
`CREATE TRIGGER IF NOT EXISTS update_ai_models_updated_at
|
||||
AFTER UPDATE ON ai_models
|
||||
BEGIN
|
||||
UPDATE ai_models SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
||||
END`,
|
||||
|
||||
`CREATE TRIGGER IF NOT EXISTS update_exchanges_updated_at
|
||||
AFTER UPDATE ON exchanges
|
||||
BEGIN
|
||||
UPDATE exchanges SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
||||
END`,
|
||||
|
||||
`CREATE TRIGGER IF NOT EXISTS update_traders_updated_at
|
||||
AFTER UPDATE ON traders
|
||||
BEGIN
|
||||
UPDATE traders SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
||||
END`,
|
||||
|
||||
`CREATE TRIGGER IF NOT EXISTS update_system_config_updated_at
|
||||
AFTER UPDATE ON system_config
|
||||
BEGIN
|
||||
UPDATE system_config SET updated_at = CURRENT_TIMESTAMP WHERE key = NEW.key;
|
||||
END`,
|
||||
}
|
||||
|
||||
for _, query := range queries {
|
||||
if _, err := d.db.Exec(query); err != nil {
|
||||
return fmt.Errorf("执行SQL失败 [%s]: %w", query, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// initDefaultData 初始化默认数据
|
||||
func (d *Database) initDefaultData() error {
|
||||
// 初始化AI模型
|
||||
aiModels := []struct {
|
||||
id, name, provider string
|
||||
}{
|
||||
{"deepseek", "DeepSeek", "deepseek"},
|
||||
{"qwen", "Qwen", "qwen"},
|
||||
}
|
||||
|
||||
for _, model := range aiModels {
|
||||
_, err := d.db.Exec(`
|
||||
INSERT OR IGNORE INTO ai_models (id, name, provider, enabled)
|
||||
VALUES (?, ?, ?, 0)
|
||||
`, model.id, model.name, model.provider)
|
||||
if err != nil {
|
||||
return fmt.Errorf("初始化AI模型失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化交易所
|
||||
exchanges := []struct {
|
||||
id, name, typ string
|
||||
}{
|
||||
{"binance", "Binance", "cex"},
|
||||
{"hyperliquid", "Hyperliquid", "dex"},
|
||||
}
|
||||
|
||||
for _, exchange := range exchanges {
|
||||
_, err := d.db.Exec(`
|
||||
INSERT OR IGNORE INTO exchanges (id, name, type, enabled)
|
||||
VALUES (?, ?, ?, 0)
|
||||
`, exchange.id, exchange.name, exchange.typ)
|
||||
if err != nil {
|
||||
return fmt.Errorf("初始化交易所失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化系统配置
|
||||
systemConfigs := map[string]string{
|
||||
"api_server_port": "8081",
|
||||
"use_default_coins": "true",
|
||||
"coin_pool_api_url": "",
|
||||
"oi_top_api_url": "",
|
||||
"max_daily_loss": "10.0",
|
||||
"max_drawdown": "20.0",
|
||||
"stop_trading_minutes": "60",
|
||||
}
|
||||
|
||||
for key, value := range systemConfigs {
|
||||
_, err := d.db.Exec(`
|
||||
INSERT OR IGNORE INTO system_config (key, value)
|
||||
VALUES (?, ?)
|
||||
`, key, value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("初始化系统配置失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AIModelConfig AI模型配置
|
||||
type AIModelConfig struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
Enabled bool `json:"enabled"`
|
||||
APIKey string `json:"apiKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ExchangeConfig 交易所配置
|
||||
type ExchangeConfig struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Enabled bool `json:"enabled"`
|
||||
APIKey string `json:"apiKey"`
|
||||
SecretKey string `json:"secretKey"`
|
||||
Testnet bool `json:"testnet"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// TraderConfig 交易员配置
|
||||
type TraderConfig struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
AIModelID string `json:"ai_model_id"`
|
||||
ExchangeID string `json:"exchange_id"`
|
||||
InitialBalance float64 `json:"initial_balance"`
|
||||
ScanIntervalMinutes int `json:"scan_interval_minutes"`
|
||||
IsRunning bool `json:"is_running"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// GetAIModels 获取所有AI模型配置
|
||||
func (d *Database) GetAIModels() ([]*AIModelConfig, error) {
|
||||
rows, err := d.db.Query(`
|
||||
SELECT id, name, provider, enabled, api_key, created_at, updated_at
|
||||
FROM ai_models ORDER BY id
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var models []*AIModelConfig
|
||||
for rows.Next() {
|
||||
var model AIModelConfig
|
||||
err := rows.Scan(
|
||||
&model.ID, &model.Name, &model.Provider,
|
||||
&model.Enabled, &model.APIKey,
|
||||
&model.CreatedAt, &model.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
models = append(models, &model)
|
||||
}
|
||||
|
||||
return models, nil
|
||||
}
|
||||
|
||||
// UpdateAIModel 更新AI模型配置
|
||||
func (d *Database) UpdateAIModel(id string, enabled bool, apiKey string) error {
|
||||
_, err := d.db.Exec(`
|
||||
UPDATE ai_models SET enabled = ?, api_key = ? WHERE id = ?
|
||||
`, enabled, apiKey, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetExchanges 获取所有交易所配置
|
||||
func (d *Database) GetExchanges() ([]*ExchangeConfig, error) {
|
||||
rows, err := d.db.Query(`
|
||||
SELECT id, name, type, enabled, api_key, secret_key, testnet, created_at, updated_at
|
||||
FROM exchanges ORDER BY id
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var exchanges []*ExchangeConfig
|
||||
for rows.Next() {
|
||||
var exchange ExchangeConfig
|
||||
err := rows.Scan(
|
||||
&exchange.ID, &exchange.Name, &exchange.Type,
|
||||
&exchange.Enabled, &exchange.APIKey, &exchange.SecretKey, &exchange.Testnet,
|
||||
&exchange.CreatedAt, &exchange.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exchanges = append(exchanges, &exchange)
|
||||
}
|
||||
|
||||
return exchanges, nil
|
||||
}
|
||||
|
||||
// UpdateExchange 更新交易所配置
|
||||
func (d *Database) UpdateExchange(id string, enabled bool, apiKey, secretKey string, testnet bool) error {
|
||||
_, err := d.db.Exec(`
|
||||
UPDATE exchanges SET enabled = ?, api_key = ?, secret_key = ?, testnet = ? WHERE id = ?
|
||||
`, enabled, apiKey, secretKey, testnet, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateTrader 创建交易员
|
||||
func (d *Database) CreateTrader(trader *TraderConfig) error {
|
||||
_, err := d.db.Exec(`
|
||||
INSERT INTO traders (id, name, ai_model_id, exchange_id, initial_balance, scan_interval_minutes, is_running)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`, trader.ID, trader.Name, trader.AIModelID, trader.ExchangeID, trader.InitialBalance, trader.ScanIntervalMinutes, trader.IsRunning)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetTraders 获取所有交易员
|
||||
func (d *Database) GetTraders() ([]*TraderConfig, error) {
|
||||
rows, err := d.db.Query(`
|
||||
SELECT id, name, ai_model_id, exchange_id, initial_balance, scan_interval_minutes, is_running, created_at, updated_at
|
||||
FROM traders ORDER BY created_at DESC
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var traders []*TraderConfig
|
||||
for rows.Next() {
|
||||
var trader TraderConfig
|
||||
err := rows.Scan(
|
||||
&trader.ID, &trader.Name, &trader.AIModelID, &trader.ExchangeID,
|
||||
&trader.InitialBalance, &trader.ScanIntervalMinutes, &trader.IsRunning,
|
||||
&trader.CreatedAt, &trader.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
traders = append(traders, &trader)
|
||||
}
|
||||
|
||||
return traders, nil
|
||||
}
|
||||
|
||||
// UpdateTraderStatus 更新交易员状态
|
||||
func (d *Database) UpdateTraderStatus(id string, isRunning bool) error {
|
||||
_, err := d.db.Exec(`UPDATE traders SET is_running = ? WHERE id = ?`, isRunning, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteTrader 删除交易员
|
||||
func (d *Database) DeleteTrader(id string) error {
|
||||
_, err := d.db.Exec(`DELETE FROM traders WHERE id = ?`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetTraderConfig 获取交易员完整配置(包含AI模型和交易所信息)
|
||||
func (d *Database) GetTraderConfig(traderID string) (*TraderConfig, *AIModelConfig, *ExchangeConfig, error) {
|
||||
var trader TraderConfig
|
||||
var aiModel AIModelConfig
|
||||
var exchange ExchangeConfig
|
||||
|
||||
err := d.db.QueryRow(`
|
||||
SELECT
|
||||
t.id, t.name, t.ai_model_id, t.exchange_id, t.initial_balance, t.scan_interval_minutes, t.is_running, t.created_at, t.updated_at,
|
||||
a.id, a.name, a.provider, a.enabled, a.api_key, a.created_at, a.updated_at,
|
||||
e.id, e.name, e.type, e.enabled, e.api_key, e.secret_key, e.testnet, e.created_at, e.updated_at
|
||||
FROM traders t
|
||||
JOIN ai_models a ON t.ai_model_id = a.id
|
||||
JOIN exchanges e ON t.exchange_id = e.id
|
||||
WHERE t.id = ?
|
||||
`, traderID).Scan(
|
||||
&trader.ID, &trader.Name, &trader.AIModelID, &trader.ExchangeID,
|
||||
&trader.InitialBalance, &trader.ScanIntervalMinutes, &trader.IsRunning,
|
||||
&trader.CreatedAt, &trader.UpdatedAt,
|
||||
&aiModel.ID, &aiModel.Name, &aiModel.Provider, &aiModel.Enabled, &aiModel.APIKey,
|
||||
&aiModel.CreatedAt, &aiModel.UpdatedAt,
|
||||
&exchange.ID, &exchange.Name, &exchange.Type, &exchange.Enabled,
|
||||
&exchange.APIKey, &exchange.SecretKey, &exchange.Testnet,
|
||||
&exchange.CreatedAt, &exchange.UpdatedAt,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return &trader, &aiModel, &exchange, nil
|
||||
}
|
||||
|
||||
// GetSystemConfig 获取系统配置
|
||||
func (d *Database) GetSystemConfig(key string) (string, error) {
|
||||
var value string
|
||||
err := d.db.QueryRow(`SELECT value FROM system_config WHERE key = ?`, key).Scan(&value)
|
||||
return value, err
|
||||
}
|
||||
|
||||
// SetSystemConfig 设置系统配置
|
||||
func (d *Database) SetSystemConfig(key, value string) error {
|
||||
_, err := d.db.Exec(`
|
||||
INSERT OR REPLACE INTO system_config (key, value) VALUES (?, ?)
|
||||
`, key, value)
|
||||
return err
|
||||
}
|
||||
|
||||
// Close 关闭数据库连接
|
||||
func (d *Database) Close() error {
|
||||
return d.db.Close()
|
||||
}
|
||||
Reference in New Issue
Block a user