Files
nofx/config/database.go
T
2025-10-30 20:51:22 +08:00

390 lines
11 KiB
Go

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()
}