1、状态更新错误处理
This commit is contained in:
		| @ -253,3 +253,30 @@ func (e LineReversePosition) ClosePositionBatch(c *gin.Context) { | |||||||
| 	} | 	} | ||||||
| 	e.OK(nil, "批量平仓成功") | 	e.OK(nil, "批量平仓成功") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // 清除所有 | ||||||
|  | func (e LineReversePosition) CleanAll(c *gin.Context) { | ||||||
|  | 	s := service.LineReversePosition{} | ||||||
|  |  | ||||||
|  | 	err := e.MakeContext(c). | ||||||
|  | 		MakeOrm(). | ||||||
|  | 		MakeService(&s.Service). | ||||||
|  | 		Errors | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Logger.Error(err) | ||||||
|  | 		e.Error(500, err, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	userId := user.GetUserId(c) | ||||||
|  | 	p := actions.GetPermissionFromContext(c) | ||||||
|  |  | ||||||
|  | 	err = s.CleanAll(p, userId) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Error(500, err, fmt.Sprintf("清除所有仓位失败,\r\n失败信息 %s", err.Error())) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.OK(nil, "清除所有仓位成功") | ||||||
|  | } | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ type LineReversePosition struct { | |||||||
| 	ReverseStatus       int             `json:"reverseStatus" gorm:"type:tinyint;comment:反单仓位状态 1-已开仓 2-已平仓"` | 	ReverseStatus       int             `json:"reverseStatus" gorm:"type:tinyint;comment:反单仓位状态 1-已开仓 2-已平仓"` | ||||||
| 	AveragePrice        decimal.Decimal `json:"averagePrice" gorm:"type:decimal(18,8);comment:主单平均价格"` | 	AveragePrice        decimal.Decimal `json:"averagePrice" gorm:"type:decimal(18,8);comment:主单平均价格"` | ||||||
| 	ReverseAveragePrice decimal.Decimal `json:"reverseAveragePrice" gorm:"type:decimal(18,8);comment:反单平均价格"` | 	ReverseAveragePrice decimal.Decimal `json:"reverseAveragePrice" gorm:"type:decimal(18,8);comment:反单平均价格"` | ||||||
|  | 	Version             int             `json:"version" gorm:"type:int;default:0;comment:版本号,用于乐观锁控制"` | ||||||
| 	models.ModelTime | 	models.ModelTime | ||||||
| 	models.ControlBy | 	models.ControlBy | ||||||
| } | } | ||||||
|  | |||||||
| @ -22,6 +22,9 @@ func registerLineReversePositionRouter(v1 *gin.RouterGroup, authMiddleware *jwt. | |||||||
| 		r.GET("/:id", actions.PermissionAction(), api.Get) | 		r.GET("/:id", actions.PermissionAction(), api.Get) | ||||||
| 		r.POST("", api.Insert) | 		r.POST("", api.Insert) | ||||||
| 		r.PUT("/:id", actions.PermissionAction(), api.Update) | 		r.PUT("/:id", actions.PermissionAction(), api.Update) | ||||||
|  |  | ||||||
|  | 		//清理所有 | ||||||
|  | 		r.DELETE("/clean-all", actions.PermissionAction(), api.CleanAll) | ||||||
| 		r.DELETE("", api.Delete) | 		r.DELETE("", api.Delete) | ||||||
|  |  | ||||||
| 		r.PUT("close/:id", actions.PermissionAction(), api.ClosePosition) | 		r.PUT("close/:id", actions.PermissionAction(), api.ClosePosition) | ||||||
|  | |||||||
| @ -25,6 +25,27 @@ type LineReversePosition struct { | |||||||
| 	service.Service | 	service.Service | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // 清除仓位记录 | ||||||
|  | func (e LineReversePosition) CleanAll(p *actions.DataPermission, userId int) error { | ||||||
|  | 	var count int64 | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Model(&models.LineReversePosition{}). | ||||||
|  | 		Where("status =1 or reverse_status = 1").Count(&count).Error; err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if count > 0 { | ||||||
|  | 		e.Log.Errorf("还有仓位无法清除") | ||||||
|  | 		return errors.New("有仓位正在进行中,不能清除所有") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := e.Orm.Exec("TRUNCATE TABLE line_reverse_position").Error; err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // 批量关闭仓位 | // 批量关闭仓位 | ||||||
| func (e LineReversePosition) ClosePositionBatch(req *dto.LineReversePositionCloseBatchReq, p *actions.DataPermission, userId int, errs *[]string) error { | func (e LineReversePosition) ClosePositionBatch(req *dto.LineReversePositionCloseBatchReq, p *actions.DataPermission, userId int, errs *[]string) error { | ||||||
| 	var positions []models.LineReversePosition | 	var positions []models.LineReversePosition | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.mod
									
									
									
									
									
								
							| @ -39,6 +39,7 @@ require ( | |||||||
| 	github.com/shirou/gopsutil/v3 v3.23.10 | 	github.com/shirou/gopsutil/v3 v3.23.10 | ||||||
| 	github.com/shopspring/decimal v1.2.0 | 	github.com/shopspring/decimal v1.2.0 | ||||||
| 	github.com/spf13/cobra v1.7.0 | 	github.com/spf13/cobra v1.7.0 | ||||||
|  | 	github.com/stretchr/testify v1.9.0 | ||||||
| 	github.com/swaggo/files v1.0.1 | 	github.com/swaggo/files v1.0.1 | ||||||
| 	github.com/swaggo/gin-swagger v1.6.0 | 	github.com/swaggo/gin-swagger v1.6.0 | ||||||
| 	github.com/swaggo/swag v1.16.2 | 	github.com/swaggo/swag v1.16.2 | ||||||
| @ -82,6 +83,7 @@ require ( | |||||||
| 	github.com/cloudwego/iasm v0.2.0 // indirect | 	github.com/cloudwego/iasm v0.2.0 // indirect | ||||||
| 	github.com/coreos/go-semver v0.3.1 // indirect | 	github.com/coreos/go-semver v0.3.1 // indirect | ||||||
| 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect | 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect | ||||||
|  | 	github.com/davecgh/go-spew v1.1.1 // indirect | ||||||
| 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect | 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect | ||||||
| 	github.com/fatih/color v1.15.0 // indirect | 	github.com/fatih/color v1.15.0 // indirect | ||||||
| 	github.com/fatih/structs v1.1.0 // indirect | 	github.com/fatih/structs v1.1.0 // indirect | ||||||
| @ -143,6 +145,7 @@ require ( | |||||||
| 	github.com/nsqio/go-nsq v1.1.0 // indirect | 	github.com/nsqio/go-nsq v1.1.0 // indirect | ||||||
| 	github.com/nyaruka/phonenumbers v1.0.55 // indirect | 	github.com/nyaruka/phonenumbers v1.0.55 // indirect | ||||||
| 	github.com/pelletier/go-toml/v2 v2.2.3 // indirect | 	github.com/pelletier/go-toml/v2 v2.2.3 // indirect | ||||||
|  | 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||||
| 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect | 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect | ||||||
| 	github.com/prometheus/client_model v0.5.0 // indirect | 	github.com/prometheus/client_model v0.5.0 // indirect | ||||||
| 	github.com/prometheus/common v0.45.0 // indirect | 	github.com/prometheus/common v0.45.0 // indirect | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestCancelFutClosePosition(t *testing.T) { | func TestCancelFutClosePosition(t *testing.T) { | ||||||
| 	dsn := "root:root@tcp(192.168.123.216:3306)/gp-bian?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms" | 	dsn := "root:123456@tcp(127.0.0.1:3306)/gp-bian?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms" | ||||||
| 	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) | 	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) | ||||||
| 	var apiUserInfo models.LineApiUser | 	var apiUserInfo models.LineApiUser | ||||||
| 	db.Model(&models.LineApiUser{}).Where("id = 21").Find(&apiUserInfo) | 	db.Model(&models.LineApiUser{}).Where("id = 21").Find(&apiUserInfo) | ||||||
|  | |||||||
							
								
								
									
										358
									
								
								services/binanceservice/config_optimized.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								services/binanceservice/config_optimized.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,358 @@ | |||||||
|  | package binanceservice | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // OptimizedConfig 优化配置结构 | ||||||
|  | type OptimizedConfig struct { | ||||||
|  | 	// 锁配置 | ||||||
|  | 	LockConfig LockConfig `json:"lock_config"` | ||||||
|  | 	 | ||||||
|  | 	// 重试配置 | ||||||
|  | 	RetryConfig RetryConfig `json:"retry_config"` | ||||||
|  | 	 | ||||||
|  | 	// 熔断器配置 | ||||||
|  | 	CircuitBreakerConfig CircuitBreakerConfig `json:"circuit_breaker_config"` | ||||||
|  | 	 | ||||||
|  | 	// 同步配置 | ||||||
|  | 	SyncConfig SyncConfig `json:"sync_config"` | ||||||
|  | 	 | ||||||
|  | 	// 性能配置 | ||||||
|  | 	PerformanceConfig PerformanceConfig `json:"performance_config"` | ||||||
|  | 	 | ||||||
|  | 	// 监控配置 | ||||||
|  | 	MonitorConfig MonitorConfig `json:"monitor_config"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LockConfig 锁配置 | ||||||
|  | type LockConfig struct { | ||||||
|  | 	// Redis分布式锁超时时间 | ||||||
|  | 	RedisLockTimeout time.Duration `json:"redis_lock_timeout"` | ||||||
|  | 	 | ||||||
|  | 	// 获取锁等待时间 | ||||||
|  | 	LockWaitTimeout time.Duration `json:"lock_wait_timeout"` | ||||||
|  | 	 | ||||||
|  | 	// 订单处理锁前缀 | ||||||
|  | 	OrderLockPrefix string `json:"order_lock_prefix"` | ||||||
|  | 	 | ||||||
|  | 	// 持仓更新锁前缀 | ||||||
|  | 	PositionLockPrefix string `json:"position_lock_prefix"` | ||||||
|  | 	 | ||||||
|  | 	// 止盈止损锁前缀 | ||||||
|  | 	TakeProfitLockPrefix string `json:"take_profit_lock_prefix"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CircuitBreakerConfig 熔断器配置 | ||||||
|  | type CircuitBreakerConfig struct { | ||||||
|  | 	Enabled           bool          `json:"enabled"` | ||||||
|  | 	FailureThreshold  int           `json:"failure_threshold"` | ||||||
|  | 	SuccessThreshold  int           `json:"success_threshold"` | ||||||
|  | 	Timeout           time.Duration `json:"timeout"` | ||||||
|  | 	HalfOpenMaxCalls  int           `json:"half_open_max_calls"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RetryConfig 重试配置 | ||||||
|  | type RetryConfig struct { | ||||||
|  | 	// 最大重试次数 | ||||||
|  | 	MaxRetries int `json:"max_retries"` | ||||||
|  | 	 | ||||||
|  | 	// 重试延迟 | ||||||
|  | 	RetryDelay time.Duration `json:"retry_delay"` | ||||||
|  | 	 | ||||||
|  | 	// 指数退避因子 | ||||||
|  | 	BackoffFactor float64 `json:"backoff_factor"` | ||||||
|  | 	 | ||||||
|  | 	// 最大重试延迟 | ||||||
|  | 	MaxRetryDelay time.Duration `json:"max_retry_delay"` | ||||||
|  | 	 | ||||||
|  | 	// API调用重试次数 | ||||||
|  | 	ApiRetryCount int `json:"api_retry_count"` | ||||||
|  | 	 | ||||||
|  | 	// 数据库操作重试次数 | ||||||
|  | 	DbRetryCount int `json:"db_retry_count"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SyncConfig 同步配置 | ||||||
|  | type SyncConfig struct { | ||||||
|  | 	// 持仓同步检查间隔 | ||||||
|  | 	PositionSyncInterval time.Duration `json:"position_sync_interval"` | ||||||
|  | 	 | ||||||
|  | 	// 持仓差异阈值 | ||||||
|  | 	PositionDiffThreshold decimal.Decimal `json:"position_diff_threshold"` | ||||||
|  | 	 | ||||||
|  | 	// 强制同步阈值 | ||||||
|  | 	ForceSyncThreshold decimal.Decimal `json:"force_sync_threshold"` | ||||||
|  | 	 | ||||||
|  | 	// 同步超时时间 | ||||||
|  | 	SyncTimeout time.Duration `json:"sync_timeout"` | ||||||
|  | 	 | ||||||
|  | 	// 是否启用自动同步 | ||||||
|  | 	AutoSyncEnabled bool `json:"auto_sync_enabled"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PerformanceConfig 性能配置 | ||||||
|  | type PerformanceConfig struct { | ||||||
|  | 	// 批量操作大小 | ||||||
|  | 	BatchSize int `json:"batch_size"` | ||||||
|  | 	 | ||||||
|  | 	// 异步处理队列大小 | ||||||
|  | 	AsyncQueueSize int `json:"async_queue_size"` | ||||||
|  | 	 | ||||||
|  | 	// 工作协程数量 | ||||||
|  | 	WorkerCount int `json:"worker_count"` | ||||||
|  | 	 | ||||||
|  | 	// 数据库连接池大小 | ||||||
|  | 	DbPoolSize int `json:"db_pool_size"` | ||||||
|  | 	 | ||||||
|  | 	// 缓存过期时间 | ||||||
|  | 	CacheExpiration time.Duration `json:"cache_expiration"` | ||||||
|  | 	 | ||||||
|  | 	// 是否启用缓存 | ||||||
|  | 	CacheEnabled bool `json:"cache_enabled"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MonitorConfig 监控配置 | ||||||
|  | type MonitorConfig struct { | ||||||
|  | 	// 是否启用监控 | ||||||
|  | 	Enabled bool `json:"enabled"` | ||||||
|  | 	 | ||||||
|  | 	// 监控数据收集间隔 | ||||||
|  | 	CollectInterval time.Duration `json:"collect_interval"` | ||||||
|  | 	 | ||||||
|  | 	// 告警阈值配置 | ||||||
|  | 	AlertThresholds AlertThresholds `json:"alert_thresholds"` | ||||||
|  | 	 | ||||||
|  | 	// 日志级别 | ||||||
|  | 	LogLevel string `json:"log_level"` | ||||||
|  | 	 | ||||||
|  | 	// 是否启用性能分析 | ||||||
|  | 	ProfileEnabled bool `json:"profile_enabled"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AlertThresholds 告警阈值配置 | ||||||
|  | type AlertThresholds struct { | ||||||
|  | 	// 订单处理失败率阈值 (百分比) | ||||||
|  | 	OrderFailureRate float64 `json:"order_failure_rate"` | ||||||
|  | 	 | ||||||
|  | 	// 持仓同步失败率阈值 (百分比) | ||||||
|  | 	PositionSyncFailureRate float64 `json:"position_sync_failure_rate"` | ||||||
|  | 	 | ||||||
|  | 	// API调用失败率阈值 (百分比) | ||||||
|  | 	ApiFailureRate float64 `json:"api_failure_rate"` | ||||||
|  | 	 | ||||||
|  | 	// 响应时间阈值 (毫秒) | ||||||
|  | 	ResponseTimeThreshold time.Duration `json:"response_time_threshold"` | ||||||
|  | 	 | ||||||
|  | 	// 内存使用率阈值 (百分比) | ||||||
|  | 	MemoryUsageThreshold float64 `json:"memory_usage_threshold"` | ||||||
|  | 	 | ||||||
|  | 	// CPU使用率阈值 (百分比) | ||||||
|  | 	CpuUsageThreshold float64 `json:"cpu_usage_threshold"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetDefaultOptimizedConfig 获取默认优化配置 | ||||||
|  | func GetDefaultOptimizedConfig() *OptimizedConfig { | ||||||
|  | 	return &OptimizedConfig{ | ||||||
|  | 		LockConfig: LockConfig{ | ||||||
|  | 			RedisLockTimeout:     30 * time.Second, | ||||||
|  | 			LockWaitTimeout:      5 * time.Second, | ||||||
|  | 			OrderLockPrefix:      "reverse_order_lock:", | ||||||
|  | 			PositionLockPrefix:   "position_update_lock:", | ||||||
|  | 			TakeProfitLockPrefix: "take_profit_lock:", | ||||||
|  | 		}, | ||||||
|  | 		RetryConfig: RetryConfig{ | ||||||
|  | 			MaxRetries:    3, | ||||||
|  | 			RetryDelay:    time.Second, | ||||||
|  | 			BackoffFactor: 2.0, | ||||||
|  | 			MaxRetryDelay: 10 * time.Second, | ||||||
|  | 			ApiRetryCount: 3, | ||||||
|  | 			DbRetryCount:  3, | ||||||
|  | 		}, | ||||||
|  | 		CircuitBreakerConfig: CircuitBreakerConfig{ | ||||||
|  | 			Enabled:          true, | ||||||
|  | 			FailureThreshold: 5, | ||||||
|  | 			SuccessThreshold: 3, | ||||||
|  | 			Timeout:          60 * time.Second, | ||||||
|  | 			HalfOpenMaxCalls: 3, | ||||||
|  | 		}, | ||||||
|  | 		SyncConfig: SyncConfig{ | ||||||
|  | 			PositionSyncInterval:  30 * time.Second, | ||||||
|  | 			PositionDiffThreshold: decimal.NewFromFloat(0.001), | ||||||
|  | 			ForceSyncThreshold:    decimal.NewFromFloat(0.01), | ||||||
|  | 			SyncTimeout:           10 * time.Second, | ||||||
|  | 			AutoSyncEnabled:       true, | ||||||
|  | 		}, | ||||||
|  | 		PerformanceConfig: PerformanceConfig{ | ||||||
|  | 			BatchSize:       10, | ||||||
|  | 			AsyncQueueSize:  1000, | ||||||
|  | 			WorkerCount:     5, | ||||||
|  | 			DbPoolSize:      20, | ||||||
|  | 			CacheExpiration: 5 * time.Minute, | ||||||
|  | 			CacheEnabled:    true, | ||||||
|  | 		}, | ||||||
|  | 		MonitorConfig: MonitorConfig{ | ||||||
|  | 			Enabled:         true, | ||||||
|  | 			CollectInterval: time.Minute, | ||||||
|  | 			AlertThresholds: AlertThresholds{ | ||||||
|  | 				OrderFailureRate:        5.0,  // 5% | ||||||
|  | 				PositionSyncFailureRate: 2.0,  // 2% | ||||||
|  | 				ApiFailureRate:          10.0, // 10% | ||||||
|  | 				ResponseTimeThreshold:   5 * time.Second, | ||||||
|  | 				MemoryUsageThreshold:    80.0, // 80% | ||||||
|  | 				CpuUsageThreshold:       70.0, // 70% | ||||||
|  | 			}, | ||||||
|  | 			LogLevel:       "info", | ||||||
|  | 			ProfileEnabled: false, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ValidateConfig 验证配置有效性 | ||||||
|  | func (c *OptimizedConfig) ValidateConfig() error { | ||||||
|  | 	// 验证锁配置 | ||||||
|  | 	if c.LockConfig.RedisLockTimeout <= 0 { | ||||||
|  | 		return fmt.Errorf("Redis锁超时时间必须大于0") | ||||||
|  | 	} | ||||||
|  | 	if c.LockConfig.LockWaitTimeout <= 0 { | ||||||
|  | 		return fmt.Errorf("锁等待超时时间必须大于0") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 验证重试配置 | ||||||
|  | 	if c.RetryConfig.MaxRetries < 0 { | ||||||
|  | 		return fmt.Errorf("最大重试次数不能为负数") | ||||||
|  | 	} | ||||||
|  | 	if c.RetryConfig.RetryDelay <= 0 { | ||||||
|  | 		return fmt.Errorf("重试延迟必须大于0") | ||||||
|  | 	} | ||||||
|  | 	if c.RetryConfig.BackoffFactor <= 1.0 { | ||||||
|  | 		return fmt.Errorf("退避因子必须大于1.0") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 验证同步配置 | ||||||
|  | 	if c.SyncConfig.PositionSyncInterval <= 0 { | ||||||
|  | 		return fmt.Errorf("持仓同步间隔必须大于0") | ||||||
|  | 	} | ||||||
|  | 	if c.SyncConfig.PositionDiffThreshold.IsNegative() { | ||||||
|  | 		return fmt.Errorf("持仓差异阈值不能为负数") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 验证性能配置 | ||||||
|  | 	if c.PerformanceConfig.BatchSize <= 0 { | ||||||
|  | 		return fmt.Errorf("批量操作大小必须大于0") | ||||||
|  | 	} | ||||||
|  | 	if c.PerformanceConfig.WorkerCount <= 0 { | ||||||
|  | 		return fmt.Errorf("工作协程数量必须大于0") | ||||||
|  | 	} | ||||||
|  | 	if c.PerformanceConfig.DbPoolSize <= 0 { | ||||||
|  | 		return fmt.Errorf("数据库连接池大小必须大于0") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 验证监控配置 | ||||||
|  | 	if c.MonitorConfig.Enabled { | ||||||
|  | 		if c.MonitorConfig.CollectInterval <= 0 { | ||||||
|  | 			return fmt.Errorf("监控数据收集间隔必须大于0") | ||||||
|  | 		} | ||||||
|  | 		if c.MonitorConfig.AlertThresholds.OrderFailureRate < 0 || c.MonitorConfig.AlertThresholds.OrderFailureRate > 100 { | ||||||
|  | 			return fmt.Errorf("订单失败率阈值必须在0-100之间") | ||||||
|  | 		} | ||||||
|  | 		if c.MonitorConfig.AlertThresholds.MemoryUsageThreshold < 0 || c.MonitorConfig.AlertThresholds.MemoryUsageThreshold > 100 { | ||||||
|  | 			return fmt.Errorf("内存使用率阈值必须在0-100之间") | ||||||
|  | 		} | ||||||
|  | 		if c.MonitorConfig.AlertThresholds.CpuUsageThreshold < 0 || c.MonitorConfig.AlertThresholds.CpuUsageThreshold > 100 { | ||||||
|  | 			return fmt.Errorf("CPU使用率阈值必须在0-100之间") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetLockKey 生成锁键名 | ||||||
|  | func (c *OptimizedConfig) GetLockKey(lockType, identifier string) string { | ||||||
|  | 	switch lockType { | ||||||
|  | 	case "order": | ||||||
|  | 		return c.LockConfig.OrderLockPrefix + identifier | ||||||
|  | 	case "position": | ||||||
|  | 		return c.LockConfig.PositionLockPrefix + identifier | ||||||
|  | 	case "take_profit": | ||||||
|  | 		return c.LockConfig.TakeProfitLockPrefix + identifier | ||||||
|  | 	default: | ||||||
|  | 		return "unknown_lock:" + identifier | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsRetryableError 判断错误是否可重试 | ||||||
|  | func (c *OptimizedConfig) IsRetryableError(err error) bool { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	errStr := err.Error() | ||||||
|  | 	// 定义不可重试的错误类型 | ||||||
|  | 	nonRetryableErrors := []string{ | ||||||
|  | 		"余额不足", | ||||||
|  | 		"订单重复", | ||||||
|  | 		"API-key", | ||||||
|  | 		"无效", | ||||||
|  | 		"权限", | ||||||
|  | 		"签名", | ||||||
|  | 		"参数错误", | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	for _, nonRetryable := range nonRetryableErrors { | ||||||
|  | 		if contains(errStr, []string{nonRetryable}) { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetRetryDelay 计算重试延迟时间 | ||||||
|  | func (c *OptimizedConfig) GetRetryDelay(attempt int) time.Duration { | ||||||
|  | 	if attempt <= 0 { | ||||||
|  | 		return c.RetryConfig.RetryDelay | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// 指数退避算法 | ||||||
|  | 	delay := c.RetryConfig.RetryDelay | ||||||
|  | 	for i := 0; i < attempt; i++ { | ||||||
|  | 		delay = time.Duration(float64(delay) * c.RetryConfig.BackoffFactor) | ||||||
|  | 		if delay > c.RetryConfig.MaxRetryDelay { | ||||||
|  | 			return c.RetryConfig.MaxRetryDelay | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return delay | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ShouldSync 判断是否需要同步持仓 | ||||||
|  | func (c *OptimizedConfig) ShouldSync(exchangePosition, systemPosition decimal.Decimal) bool { | ||||||
|  | 	diff := exchangePosition.Sub(systemPosition).Abs() | ||||||
|  | 	return diff.GreaterThan(c.SyncConfig.PositionDiffThreshold) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ShouldForceSync 判断是否需要强制同步 | ||||||
|  | func (c *OptimizedConfig) ShouldForceSync(exchangePosition, systemPosition decimal.Decimal) bool { | ||||||
|  | 	diff := exchangePosition.Sub(systemPosition).Abs() | ||||||
|  | 	return diff.GreaterThan(c.SyncConfig.ForceSyncThreshold) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 全局配置实例 | ||||||
|  | var GlobalOptimizedConfig *OptimizedConfig | ||||||
|  |  | ||||||
|  | // InitOptimizedConfig 初始化优化配置 | ||||||
|  | func InitOptimizedConfig() error { | ||||||
|  | 	GlobalOptimizedConfig = GetDefaultOptimizedConfig() | ||||||
|  | 	return GlobalOptimizedConfig.ValidateConfig() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetOptimizedConfig 获取全局优化配置 | ||||||
|  | func GetOptimizedConfig() *OptimizedConfig { | ||||||
|  | 	if GlobalOptimizedConfig == nil { | ||||||
|  | 		InitOptimizedConfig() | ||||||
|  | 	} | ||||||
|  | 	return GlobalOptimizedConfig | ||||||
|  | } | ||||||
							
								
								
									
										676
									
								
								services/binanceservice/error_handler_optimized.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										676
									
								
								services/binanceservice/error_handler_optimized.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,676 @@ | |||||||
|  | package binanceservice | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/logger" | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // ErrorType 错误类型 | ||||||
|  | type ErrorType string | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	ErrorTypeNetwork    ErrorType = "network" | ||||||
|  | 	ErrorTypeDatabase   ErrorType = "database" | ||||||
|  | 	ErrorTypeBusiness   ErrorType = "business" | ||||||
|  | 	ErrorTypeSystem     ErrorType = "system" | ||||||
|  | 	ErrorTypeValidation ErrorType = "validation" | ||||||
|  | 	ErrorTypeTimeout    ErrorType = "timeout" | ||||||
|  | 	ErrorTypeRateLimit  ErrorType = "rate_limit" | ||||||
|  | 	ErrorTypeAuth       ErrorType = "auth" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // ErrorSeverity 错误严重程度 | ||||||
|  | type ErrorSeverity string | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	SeverityLow      ErrorSeverity = "low" | ||||||
|  | 	SeverityMedium   ErrorSeverity = "medium" | ||||||
|  | 	SeverityHigh     ErrorSeverity = "high" | ||||||
|  | 	SeverityCritical ErrorSeverity = "critical" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // ErrorAction 错误处理动作 | ||||||
|  | type ErrorAction string | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	ActionRetry     ErrorAction = "retry" | ||||||
|  | 	ActionFallback  ErrorAction = "fallback" | ||||||
|  | 	ActionAlert     ErrorAction = "alert" | ||||||
|  | 	ActionIgnore    ErrorAction = "ignore" | ||||||
|  | 	ActionCircuit   ErrorAction = "circuit" | ||||||
|  | 	ActionDegrade   ErrorAction = "degrade" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // ErrorInfo 错误信息 | ||||||
|  | type ErrorInfo struct { | ||||||
|  | 	Type        ErrorType     `json:"type"` | ||||||
|  | 	Severity    ErrorSeverity `json:"severity"` | ||||||
|  | 	Message     string        `json:"message"` | ||||||
|  | 	OriginalErr error         `json:"-"` | ||||||
|  | 	Context     string        `json:"context"` | ||||||
|  | 	Timestamp   time.Time     `json:"timestamp"` | ||||||
|  | 	Retryable   bool          `json:"retryable"` | ||||||
|  | 	Action      ErrorAction   `json:"action"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RetryConfig 和 CircuitBreakerConfig 已在 config_optimized.go 中定义 | ||||||
|  |  | ||||||
|  | // ErrorHandler 错误处理器 | ||||||
|  | type ErrorHandler struct { | ||||||
|  | 	config           *OptimizedConfig | ||||||
|  | 	metricsCollector *MetricsCollector | ||||||
|  | 	circuitBreakers  map[string]*CircuitBreaker | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CircuitBreaker 熔断器 | ||||||
|  | type CircuitBreaker struct { | ||||||
|  | 	name         string | ||||||
|  | 	config       CircuitBreakerConfig | ||||||
|  | 	state        CircuitState | ||||||
|  | 	failureCount int | ||||||
|  | 	successCount int | ||||||
|  | 	lastFailTime time.Time | ||||||
|  | 	nextRetry    time.Time | ||||||
|  | 	halfOpenCalls int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CircuitState 熔断器状态 | ||||||
|  | type CircuitState string | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	StateClosed   CircuitState = "closed" | ||||||
|  | 	StateOpen     CircuitState = "open" | ||||||
|  | 	StateHalfOpen CircuitState = "half_open" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // NewErrorHandler 创建错误处理器 | ||||||
|  | func NewErrorHandler(config *OptimizedConfig, metricsCollector *MetricsCollector) *ErrorHandler { | ||||||
|  | 	return &ErrorHandler{ | ||||||
|  | 		config:           config, | ||||||
|  | 		metricsCollector: metricsCollector, | ||||||
|  | 		circuitBreakers:  make(map[string]*CircuitBreaker), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ClassifyError 分类错误 | ||||||
|  | func (eh *ErrorHandler) ClassifyError(err error, context string) *ErrorInfo { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	errorInfo := &ErrorInfo{ | ||||||
|  | 		OriginalErr: err, | ||||||
|  | 		Message:     err.Error(), | ||||||
|  | 		Context:     context, | ||||||
|  | 		Timestamp:   time.Now(), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 根据错误类型分类 | ||||||
|  | 	switch { | ||||||
|  | 	case isNetworkError(err): | ||||||
|  | 		errorInfo.Type = ErrorTypeNetwork | ||||||
|  | 		errorInfo.Severity = SeverityMedium | ||||||
|  | 		errorInfo.Retryable = true | ||||||
|  | 		errorInfo.Action = ActionRetry | ||||||
|  |  | ||||||
|  | 	case isDatabaseError(err): | ||||||
|  | 		errorInfo.Type = ErrorTypeDatabase | ||||||
|  | 		errorInfo.Severity = SeverityHigh | ||||||
|  | 		errorInfo.Retryable = isRetryableDatabaseError(err) | ||||||
|  | 		errorInfo.Action = ActionRetry | ||||||
|  |  | ||||||
|  | 	case isTimeoutError(err): | ||||||
|  | 		errorInfo.Type = ErrorTypeTimeout | ||||||
|  | 		errorInfo.Severity = SeverityMedium | ||||||
|  | 		errorInfo.Retryable = true | ||||||
|  | 		errorInfo.Action = ActionRetry | ||||||
|  |  | ||||||
|  | 	case isRateLimitError(err): | ||||||
|  | 		errorInfo.Type = ErrorTypeRateLimit | ||||||
|  | 		errorInfo.Severity = SeverityMedium | ||||||
|  | 		errorInfo.Retryable = true | ||||||
|  | 		errorInfo.Action = ActionRetry | ||||||
|  |  | ||||||
|  | 	case isAuthError(err): | ||||||
|  | 		errorInfo.Type = ErrorTypeAuth | ||||||
|  | 		errorInfo.Severity = SeverityHigh | ||||||
|  | 		errorInfo.Retryable = false | ||||||
|  | 		errorInfo.Action = ActionAlert | ||||||
|  |  | ||||||
|  | 	case isValidationError(err): | ||||||
|  | 		errorInfo.Type = ErrorTypeValidation | ||||||
|  | 		errorInfo.Severity = SeverityLow | ||||||
|  | 		errorInfo.Retryable = false | ||||||
|  | 		errorInfo.Action = ActionIgnore | ||||||
|  |  | ||||||
|  | 	case isBusinessError(err): | ||||||
|  | 		errorInfo.Type = ErrorTypeBusiness | ||||||
|  | 		errorInfo.Severity = SeverityMedium | ||||||
|  | 		errorInfo.Retryable = false | ||||||
|  | 		errorInfo.Action = ActionFallback | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		errorInfo.Type = ErrorTypeSystem | ||||||
|  | 		errorInfo.Severity = SeverityHigh | ||||||
|  | 		errorInfo.Retryable = true | ||||||
|  | 		errorInfo.Action = ActionRetry | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return errorInfo | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HandleError 处理错误 | ||||||
|  | func (eh *ErrorHandler) HandleError(err error, context string) *ErrorInfo { | ||||||
|  | 	errorInfo := eh.ClassifyError(err, context) | ||||||
|  | 	if errorInfo == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 记录错误指标 | ||||||
|  | 	if eh.metricsCollector != nil { | ||||||
|  | 		eh.metricsCollector.RecordError(string(errorInfo.Type), errorInfo.Message, context) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 根据错误动作处理 | ||||||
|  | 	switch errorInfo.Action { | ||||||
|  | 	case ActionAlert: | ||||||
|  | 		eh.sendAlert(errorInfo) | ||||||
|  | 	case ActionCircuit: | ||||||
|  | 		eh.triggerCircuitBreaker(context) | ||||||
|  | 	case ActionDegrade: | ||||||
|  | 		eh.enableDegradedMode(context) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 记录日志 | ||||||
|  | 	eh.logError(errorInfo) | ||||||
|  |  | ||||||
|  | 	return errorInfo | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RetryWithBackoff 带退避的重试 | ||||||
|  | func (eh *ErrorHandler) RetryWithBackoff(ctx context.Context, operation func() error, config RetryConfig, context string) error { | ||||||
|  | 	var lastErr error | ||||||
|  | 	delay := config.RetryDelay | ||||||
|  |  | ||||||
|  | 	for attempt := 1; attempt <= config.MaxRetries; attempt++ { | ||||||
|  | 		// 检查上下文是否已取消 | ||||||
|  | 		select { | ||||||
|  | 		case <-ctx.Done(): | ||||||
|  | 			return ctx.Err() | ||||||
|  | 		default: | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 检查熔断器状态 | ||||||
|  | 		if !eh.canExecute(context) { | ||||||
|  | 			return fmt.Errorf("circuit breaker is open for %s", context) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 执行操作 | ||||||
|  | 		// 注意:config中没有TimeoutPerTry字段,这里暂时注释掉 | ||||||
|  | 		// if config.TimeoutPerTry > 0 { | ||||||
|  | 		//	var cancel context.CancelFunc | ||||||
|  | 		//	operationCtx, cancel = context.WithTimeout(ctx, config.TimeoutPerTry) | ||||||
|  | 		//	defer cancel() | ||||||
|  | 		// } | ||||||
|  |  | ||||||
|  | 		err := operation() | ||||||
|  | 		if err == nil { | ||||||
|  | 			// 成功,记录成功指标 | ||||||
|  | 			if eh.metricsCollector != nil { | ||||||
|  | 				eh.metricsCollector.RecordRetry(true) | ||||||
|  | 			} | ||||||
|  | 			eh.recordSuccess(context) | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		lastErr = err | ||||||
|  | 		errorInfo := eh.ClassifyError(err, context) | ||||||
|  |  | ||||||
|  | 		// 记录失败 | ||||||
|  | 		eh.recordFailure(context) | ||||||
|  |  | ||||||
|  | 		// 如果不可重试,直接返回 | ||||||
|  | 		if errorInfo != nil && !errorInfo.Retryable { | ||||||
|  | 			logger.Warnf("不可重试的错误 [%s]: %v", context, err) | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 最后一次尝试,不再等待 | ||||||
|  | 		if attempt == config.MaxRetries { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 计算下次重试延迟 | ||||||
|  | 		nextDelay := eh.calculateDelay(delay, config) | ||||||
|  | 		logger.Infof("重试 %d/%d [%s] 在 %v 后,错误: %v", attempt, config.MaxRetries, context, nextDelay, err) | ||||||
|  |  | ||||||
|  | 		// 等待重试 | ||||||
|  | 		select { | ||||||
|  | 		case <-ctx.Done(): | ||||||
|  | 			return ctx.Err() | ||||||
|  | 		case <-time.After(nextDelay): | ||||||
|  | 			delay = nextDelay | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 记录重试失败指标 | ||||||
|  | 	if eh.metricsCollector != nil { | ||||||
|  | 		eh.metricsCollector.RecordRetry(false) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return fmt.Errorf("重试 %d 次后仍然失败 [%s]: %w", config.MaxRetries, context, lastErr) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // calculateDelay 计算重试延迟 | ||||||
|  | func (eh *ErrorHandler) calculateDelay(currentDelay time.Duration, config RetryConfig) time.Duration { | ||||||
|  | 	nextDelay := time.Duration(float64(currentDelay) * config.BackoffFactor) | ||||||
|  |  | ||||||
|  | 	// 限制最大延迟 | ||||||
|  | 	if nextDelay > config.MaxRetryDelay { | ||||||
|  | 		nextDelay = config.MaxRetryDelay | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 添加10%抖动(简化版本,不依赖JitterEnabled字段) | ||||||
|  | 	jitter := time.Duration(float64(nextDelay) * 0.1) | ||||||
|  | 	if jitter > 0 { | ||||||
|  | 		nextDelay += time.Duration(time.Now().UnixNano() % int64(jitter)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nextDelay | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // canExecute 检查是否可以执行操作(熔断器检查) | ||||||
|  | func (eh *ErrorHandler) canExecute(context string) bool { | ||||||
|  | 	cb := eh.getCircuitBreaker(context) | ||||||
|  | 	if cb == nil || !cb.config.Enabled { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	now := time.Now() | ||||||
|  |  | ||||||
|  | 	switch cb.state { | ||||||
|  | 	case StateClosed: | ||||||
|  | 		return true | ||||||
|  |  | ||||||
|  | 	case StateOpen: | ||||||
|  | 		if now.After(cb.nextRetry) { | ||||||
|  | 			cb.state = StateHalfOpen | ||||||
|  | 			cb.halfOpenCalls = 0 | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 		return false | ||||||
|  |  | ||||||
|  | 	case StateHalfOpen: | ||||||
|  | 		return cb.halfOpenCalls < cb.config.HalfOpenMaxCalls | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // recordSuccess 记录成功 | ||||||
|  | func (eh *ErrorHandler) recordSuccess(context string) { | ||||||
|  | 	cb := eh.getCircuitBreaker(context) | ||||||
|  | 	if cb == nil || !cb.config.Enabled { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch cb.state { | ||||||
|  | 	case StateHalfOpen: | ||||||
|  | 		cb.successCount++ | ||||||
|  | 		if cb.successCount >= cb.config.SuccessThreshold { | ||||||
|  | 			cb.state = StateClosed | ||||||
|  | 			cb.failureCount = 0 | ||||||
|  | 			cb.successCount = 0 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	case StateClosed: | ||||||
|  | 		cb.failureCount = 0 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // recordFailure 记录失败 | ||||||
|  | func (eh *ErrorHandler) recordFailure(context string) { | ||||||
|  | 	cb := eh.getCircuitBreaker(context) | ||||||
|  | 	if cb == nil || !cb.config.Enabled { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cb.failureCount++ | ||||||
|  | 	cb.lastFailTime = time.Now() | ||||||
|  |  | ||||||
|  | 	switch cb.state { | ||||||
|  | 	case StateClosed: | ||||||
|  | 		if cb.failureCount >= cb.config.FailureThreshold { | ||||||
|  | 			cb.state = StateOpen | ||||||
|  | 			cb.nextRetry = time.Now().Add(cb.config.Timeout) | ||||||
|  | 			logger.Warnf("熔断器开启 [%s]: 失败次数 %d", context, cb.failureCount) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	case StateHalfOpen: | ||||||
|  | 		cb.state = StateOpen | ||||||
|  | 		cb.nextRetry = time.Now().Add(cb.config.Timeout) | ||||||
|  | 		cb.successCount = 0 | ||||||
|  | 		logger.Warnf("熔断器重新开启 [%s]", context) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // getCircuitBreaker 获取熔断器 | ||||||
|  | func (eh *ErrorHandler) getCircuitBreaker(context string) *CircuitBreaker { | ||||||
|  | 	cb, exists := eh.circuitBreakers[context] | ||||||
|  | 	if !exists { | ||||||
|  | 		cb = &CircuitBreaker{ | ||||||
|  | 			name:   context, | ||||||
|  | 			config: eh.config.CircuitBreakerConfig, | ||||||
|  | 			state:  StateClosed, | ||||||
|  | 		} | ||||||
|  | 		eh.circuitBreakers[context] = cb | ||||||
|  | 	} | ||||||
|  | 	return cb | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // triggerCircuitBreaker 触发熔断器 | ||||||
|  | func (eh *ErrorHandler) triggerCircuitBreaker(context string) { | ||||||
|  | 	cb := eh.getCircuitBreaker(context) | ||||||
|  | 	if cb != nil && cb.config.Enabled { | ||||||
|  | 		cb.state = StateOpen | ||||||
|  | 		cb.nextRetry = time.Now().Add(cb.config.Timeout) | ||||||
|  | 		logger.Warnf("手动触发熔断器 [%s]", context) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // sendAlert 发送告警 | ||||||
|  | func (eh *ErrorHandler) sendAlert(errorInfo *ErrorInfo) { | ||||||
|  | 	// 这里可以集成告警系统,如钉钉、邮件等 | ||||||
|  | 	logger.Errorf("告警: [%s] %s - %s", errorInfo.Type, errorInfo.Context, errorInfo.Message) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // enableDegradedMode 启用降级模式 | ||||||
|  | func (eh *ErrorHandler) enableDegradedMode(context string) { | ||||||
|  | 	logger.Warnf("启用降级模式 [%s]", context) | ||||||
|  | 	// 这里可以实现具体的降级逻辑 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // logError 记录错误日志 | ||||||
|  | func (eh *ErrorHandler) logError(errorInfo *ErrorInfo) { | ||||||
|  | 	switch errorInfo.Severity { | ||||||
|  | 	case SeverityLow: | ||||||
|  | 		logger.Infof("[%s] %s: %s", errorInfo.Type, errorInfo.Context, errorInfo.Message) | ||||||
|  | 	case SeverityMedium: | ||||||
|  | 		logger.Warnf("[%s] %s: %s", errorInfo.Type, errorInfo.Context, errorInfo.Message) | ||||||
|  | 	case SeverityHigh, SeverityCritical: | ||||||
|  | 		logger.Errorf("[%s] %s: %s", errorInfo.Type, errorInfo.Context, errorInfo.Message) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetCircuitBreakerStatus 获取熔断器状态 | ||||||
|  | func (eh *ErrorHandler) GetCircuitBreakerStatus() map[string]interface{} { | ||||||
|  | 	status := make(map[string]interface{}) | ||||||
|  | 	for name, cb := range eh.circuitBreakers { | ||||||
|  | 		status[name] = map[string]interface{}{ | ||||||
|  | 			"state":         cb.state, | ||||||
|  | 			"failure_count": cb.failureCount, | ||||||
|  | 			"success_count": cb.successCount, | ||||||
|  | 			"last_fail_time": cb.lastFailTime, | ||||||
|  | 			"next_retry":     cb.nextRetry, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return status | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 错误分类辅助函数 | ||||||
|  |  | ||||||
|  | // isNetworkError 判断是否为网络错误 | ||||||
|  | func isNetworkError(err error) bool { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 检查网络相关错误 | ||||||
|  | 	var netErr net.Error | ||||||
|  | 	if errors.As(err, &netErr) { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 检查常见网络错误消息 | ||||||
|  | 	errorMsg := strings.ToLower(err.Error()) | ||||||
|  | 	networkKeywords := []string{ | ||||||
|  | 		"connection refused", | ||||||
|  | 		"connection reset", | ||||||
|  | 		"connection timeout", | ||||||
|  | 		"network unreachable", | ||||||
|  | 		"no route to host", | ||||||
|  | 		"dns", | ||||||
|  | 		"socket", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, keyword := range networkKeywords { | ||||||
|  | 		if strings.Contains(errorMsg, keyword) { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isDatabaseError 判断是否为数据库错误 | ||||||
|  | func isDatabaseError(err error) bool { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 检查GORM错误 | ||||||
|  | 	if errors.Is(err, gorm.ErrRecordNotFound) || | ||||||
|  | 		errors.Is(err, gorm.ErrInvalidTransaction) || | ||||||
|  | 		errors.Is(err, gorm.ErrNotImplemented) { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 检查数据库相关错误消息 | ||||||
|  | 	errorMsg := strings.ToLower(err.Error()) | ||||||
|  | 	dbKeywords := []string{ | ||||||
|  | 		"database", | ||||||
|  | 		"sql", | ||||||
|  | 		"mysql", | ||||||
|  | 		"postgres", | ||||||
|  | 		"connection pool", | ||||||
|  | 		"deadlock", | ||||||
|  | 		"constraint", | ||||||
|  | 		"duplicate key", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, keyword := range dbKeywords { | ||||||
|  | 		if strings.Contains(errorMsg, keyword) { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isRetryableDatabaseError 判断是否为可重试的数据库错误 | ||||||
|  | func isRetryableDatabaseError(err error) bool { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 不可重试的错误 | ||||||
|  | 	if errors.Is(err, gorm.ErrRecordNotFound) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	errorMsg := strings.ToLower(err.Error()) | ||||||
|  | 	nonRetryableKeywords := []string{ | ||||||
|  | 		"constraint", | ||||||
|  | 		"duplicate key", | ||||||
|  | 		"foreign key", | ||||||
|  | 		"syntax error", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, keyword := range nonRetryableKeywords { | ||||||
|  | 		if strings.Contains(errorMsg, keyword) { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isTimeoutError 判断是否为超时错误 | ||||||
|  | func isTimeoutError(err error) bool { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 检查超时错误 | ||||||
|  | 	var netErr net.Error | ||||||
|  | 	if errors.As(err, &netErr) && netErr.Timeout() { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if errors.Is(err, context.DeadlineExceeded) { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	errorMsg := strings.ToLower(err.Error()) | ||||||
|  | 	return strings.Contains(errorMsg, "timeout") || | ||||||
|  | 		strings.Contains(errorMsg, "deadline exceeded") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isRateLimitError 判断是否为限流错误 | ||||||
|  | func isRateLimitError(err error) bool { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	errorMsg := strings.ToLower(err.Error()) | ||||||
|  | 	rateLimitKeywords := []string{ | ||||||
|  | 		"rate limit", | ||||||
|  | 		"too many requests", | ||||||
|  | 		"429", | ||||||
|  | 		"quota exceeded", | ||||||
|  | 		"throttle", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, keyword := range rateLimitKeywords { | ||||||
|  | 		if strings.Contains(errorMsg, keyword) { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isAuthError 判断是否为认证错误 | ||||||
|  | func isAuthError(err error) bool { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	errorMsg := strings.ToLower(err.Error()) | ||||||
|  | 	authKeywords := []string{ | ||||||
|  | 		"unauthorized", | ||||||
|  | 		"authentication", | ||||||
|  | 		"invalid signature", | ||||||
|  | 		"api key", | ||||||
|  | 		"401", | ||||||
|  | 		"403", | ||||||
|  | 		"forbidden", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, keyword := range authKeywords { | ||||||
|  | 		if strings.Contains(errorMsg, keyword) { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isValidationError 判断是否为验证错误 | ||||||
|  | func isValidationError(err error) bool { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	errorMsg := strings.ToLower(err.Error()) | ||||||
|  | 	validationKeywords := []string{ | ||||||
|  | 		"validation", | ||||||
|  | 		"invalid parameter", | ||||||
|  | 		"bad request", | ||||||
|  | 		"400", | ||||||
|  | 		"missing required", | ||||||
|  | 		"invalid format", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, keyword := range validationKeywords { | ||||||
|  | 		if strings.Contains(errorMsg, keyword) { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isBusinessError 判断是否为业务错误 | ||||||
|  | func isBusinessError(err error) bool { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	errorMsg := strings.ToLower(err.Error()) | ||||||
|  | 	businessKeywords := []string{ | ||||||
|  | 		"insufficient balance", | ||||||
|  | 		"order not found", | ||||||
|  | 		"position not found", | ||||||
|  | 		"invalid order status", | ||||||
|  | 		"market closed", | ||||||
|  | 		"symbol not found", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, keyword := range businessKeywords { | ||||||
|  | 		if strings.Contains(errorMsg, keyword) { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 全局错误处理器实例 | ||||||
|  | var GlobalErrorHandler *ErrorHandler | ||||||
|  |  | ||||||
|  | // InitErrorHandler 初始化错误处理器 | ||||||
|  | func InitErrorHandler(config *OptimizedConfig, metricsCollector *MetricsCollector) { | ||||||
|  | 	GlobalErrorHandler = NewErrorHandler(config, metricsCollector) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetErrorHandler 获取全局错误处理器 | ||||||
|  | func GetErrorHandler() *ErrorHandler { | ||||||
|  | 	return GlobalErrorHandler | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HandleErrorWithRetry 处理错误并重试的便捷函数 | ||||||
|  | func HandleErrorWithRetry(ctx context.Context, operation func() error, context string) error { | ||||||
|  | 	if GlobalErrorHandler == nil { | ||||||
|  | 		return operation() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	config := RetryConfig{ | ||||||
|  | 		MaxRetries:     3, | ||||||
|  | 		RetryDelay:     100 * time.Millisecond, | ||||||
|  | 		MaxRetryDelay:  5 * time.Second, | ||||||
|  | 		BackoffFactor:  2.0, | ||||||
|  | 		ApiRetryCount:  3, | ||||||
|  | 		DbRetryCount:   3, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return GlobalErrorHandler.RetryWithBackoff(ctx, operation, config, context) | ||||||
|  | } | ||||||
							
								
								
									
										587
									
								
								services/binanceservice/integration_test_optimized.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										587
									
								
								services/binanceservice/integration_test_optimized.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,587 @@ | |||||||
|  | package binanceservice | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"sync" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/logger" | ||||||
|  | 	"github.com/go-redis/redis/v8" | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | 	"github.com/stretchr/testify/suite" | ||||||
|  | 	"gorm.io/driver/mysql" | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // OptimizedSystemTestSuite 优化系统测试套件 | ||||||
|  | type OptimizedSystemTestSuite struct { | ||||||
|  | 	suite.Suite | ||||||
|  | 	db              *gorm.DB | ||||||
|  | 	redisClient     *redis.Client | ||||||
|  | 	config          *OptimizedConfig | ||||||
|  | 	metricsCollector *MetricsCollector | ||||||
|  | 	errorHandler    *ErrorHandler | ||||||
|  | 	lockManager     *LockManager | ||||||
|  | 	txManager       *TransactionManager | ||||||
|  | 	reverseService  *ReverseServiceOptimized | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetupSuite 设置测试套件 | ||||||
|  | func (suite *OptimizedSystemTestSuite) SetupSuite() { | ||||||
|  | 	// 初始化数据库连接(测试环境) | ||||||
|  | 	dsn := "root:password@tcp(localhost:3306)/test_exchange?charset=utf8mb4&parseTime=True&loc=Local" | ||||||
|  | 	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		suite.T().Skip("跳过集成测试:无法连接数据库") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	suite.db = db | ||||||
|  |  | ||||||
|  | 	// 初始化Redis连接(测试环境) | ||||||
|  | 	suite.redisClient = redis.NewClient(&redis.Options{ | ||||||
|  | 		Addr:     "localhost:6379", | ||||||
|  | 		Password: "", | ||||||
|  | 		DB:       1, // 使用测试数据库 | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// 测试Redis连接 | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 	if err := suite.redisClient.Ping(ctx).Err(); err != nil { | ||||||
|  | 		suite.T().Skip("跳过集成测试:无法连接Redis") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 初始化配置 | ||||||
|  | 	suite.config = GetDefaultOptimizedConfig() | ||||||
|  | 	suite.config.MonitorConfig.Enabled = true | ||||||
|  |  | ||||||
|  | 	// 初始化组件 | ||||||
|  | 	suite.metricsCollector = NewMetricsCollector(suite.config) | ||||||
|  | 	suite.errorHandler = NewErrorHandler(suite.config, suite.metricsCollector) | ||||||
|  | 	suite.lockManager = NewLockManager(suite.config, suite.redisClient, suite.metricsCollector) | ||||||
|  | 	suite.txManager = NewTransactionManager(suite.db, suite.metricsCollector, suite.config) | ||||||
|  |  | ||||||
|  | 	// 启动指标收集 | ||||||
|  | 	suite.metricsCollector.Start() | ||||||
|  |  | ||||||
|  | 	// 创建测试表 | ||||||
|  | 	suite.createTestTables() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TearDownSuite 清理测试套件 | ||||||
|  | func (suite *OptimizedSystemTestSuite) TearDownSuite() { | ||||||
|  | 	if suite.metricsCollector != nil { | ||||||
|  | 		suite.metricsCollector.Stop() | ||||||
|  | 	} | ||||||
|  | 	if suite.redisClient != nil { | ||||||
|  | 		suite.redisClient.Close() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetupTest 设置每个测试 | ||||||
|  | func (suite *OptimizedSystemTestSuite) SetupTest() { | ||||||
|  | 	// 清理Redis测试数据 | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 	suite.redisClient.FlushDB(ctx) | ||||||
|  |  | ||||||
|  | 	// 清理数据库测试数据 | ||||||
|  | 	suite.db.Exec("DELETE FROM test_orders") | ||||||
|  | 	suite.db.Exec("DELETE FROM test_positions") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // createTestTables 创建测试表 | ||||||
|  | func (suite *OptimizedSystemTestSuite) createTestTables() { | ||||||
|  | 	// 创建测试订单表 | ||||||
|  | 	suite.db.Exec(` | ||||||
|  | 		CREATE TABLE IF NOT EXISTS test_orders ( | ||||||
|  | 			id BIGINT PRIMARY KEY AUTO_INCREMENT, | ||||||
|  | 			order_id VARCHAR(100) UNIQUE NOT NULL, | ||||||
|  | 			user_id VARCHAR(50) NOT NULL, | ||||||
|  | 			symbol VARCHAR(20) NOT NULL, | ||||||
|  | 			side VARCHAR(10) NOT NULL, | ||||||
|  | 			quantity DECIMAL(20,8) NOT NULL, | ||||||
|  | 			price DECIMAL(20,8), | ||||||
|  | 			status VARCHAR(20) NOT NULL, | ||||||
|  | 			version INT DEFAULT 0, | ||||||
|  | 			created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | ||||||
|  | 			updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||||
|  | 			INDEX idx_user_symbol (user_id, symbol), | ||||||
|  | 			INDEX idx_status (status), | ||||||
|  | 			INDEX idx_version (version) | ||||||
|  | 		) | ||||||
|  | 	`) | ||||||
|  |  | ||||||
|  | 	// 创建测试持仓表 | ||||||
|  | 	suite.db.Exec(` | ||||||
|  | 		CREATE TABLE IF NOT EXISTS test_positions ( | ||||||
|  | 			id BIGINT PRIMARY KEY AUTO_INCREMENT, | ||||||
|  | 			user_id VARCHAR(50) NOT NULL, | ||||||
|  | 			symbol VARCHAR(20) NOT NULL, | ||||||
|  | 			side VARCHAR(10) NOT NULL, | ||||||
|  | 			quantity DECIMAL(20,8) NOT NULL, | ||||||
|  | 			avg_price DECIMAL(20,8), | ||||||
|  | 			status VARCHAR(20) NOT NULL, | ||||||
|  | 			version INT DEFAULT 0, | ||||||
|  | 			created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | ||||||
|  | 			updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||||
|  | 			UNIQUE KEY uk_user_symbol_side (user_id, symbol, side), | ||||||
|  | 			INDEX idx_version (version) | ||||||
|  | 		) | ||||||
|  | 	`) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestLockManager 测试锁管理器 | ||||||
|  | func (suite *OptimizedSystemTestSuite) TestLockManager() { | ||||||
|  | 	ctx := context.Background() | ||||||
|  |  | ||||||
|  | 	// 测试分布式锁 | ||||||
|  | 	suite.Run("DistributedLock", func() { | ||||||
|  | 		config := LockManagerConfig{ | ||||||
|  | 			Type:       LockTypeDistributed, | ||||||
|  | 			Scope:      ScopeOrder, | ||||||
|  | 			Key:        "test_order_123", | ||||||
|  | 			Expiration: 10 * time.Second, | ||||||
|  | 			Timeout:    5 * time.Second, | ||||||
|  | 			RetryDelay: 100 * time.Millisecond, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 获取锁 | ||||||
|  | 		lock, err := suite.lockManager.AcquireLock(ctx, config) | ||||||
|  | 		assert.NoError(suite.T(), err) | ||||||
|  | 		assert.NotNil(suite.T(), lock) | ||||||
|  | 		assert.True(suite.T(), lock.IsLocked()) | ||||||
|  |  | ||||||
|  | 		// 尝试获取同一个锁(应该失败) | ||||||
|  | 		config.Timeout = 1 * time.Second | ||||||
|  | 		_, err = suite.lockManager.AcquireLock(ctx, config) | ||||||
|  | 		assert.Error(suite.T(), err) | ||||||
|  |  | ||||||
|  | 		// 释放锁 | ||||||
|  | 		err = suite.lockManager.ReleaseLock(ctx, lock) | ||||||
|  | 		assert.NoError(suite.T(), err) | ||||||
|  | 		assert.False(suite.T(), lock.IsLocked()) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// 测试本地锁 | ||||||
|  | 	suite.Run("LocalLock", func() { | ||||||
|  | 		config := LockManagerConfig{ | ||||||
|  | 			Type:       LockTypeLocal, | ||||||
|  | 			Scope:      ScopeUser, | ||||||
|  | 			Key:        "test_user_456", | ||||||
|  | 			Expiration: 10 * time.Second, | ||||||
|  | 			Timeout:    5 * time.Second, | ||||||
|  | 			RetryDelay: 100 * time.Millisecond, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		lock, err := suite.lockManager.AcquireLock(ctx, config) | ||||||
|  | 		assert.NoError(suite.T(), err) | ||||||
|  | 		assert.NotNil(suite.T(), lock) | ||||||
|  | 		assert.True(suite.T(), lock.IsLocked()) | ||||||
|  |  | ||||||
|  | 		err = suite.lockManager.ReleaseLock(ctx, lock) | ||||||
|  | 		assert.NoError(suite.T(), err) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// 测试并发锁 | ||||||
|  | 	suite.Run("ConcurrentLocks", func() { | ||||||
|  | 		var wg sync.WaitGroup | ||||||
|  | 		successCount := 0 | ||||||
|  | 		var mu sync.Mutex | ||||||
|  |  | ||||||
|  | 		for i := 0; i < 10; i++ { | ||||||
|  | 			wg.Add(1) | ||||||
|  | 			go func(id int) { | ||||||
|  | 				defer wg.Done() | ||||||
|  |  | ||||||
|  | 				config := LockManagerConfig{ | ||||||
|  | 					Type:       LockTypeDistributed, | ||||||
|  | 					Scope:      ScopeGlobal, | ||||||
|  | 					Key:        "concurrent_test", | ||||||
|  | 					Expiration: 2 * time.Second, | ||||||
|  | 					Timeout:    1 * time.Second, | ||||||
|  | 					RetryDelay: 50 * time.Millisecond, | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				lock, err := suite.lockManager.AcquireLock(ctx, config) | ||||||
|  | 				if err == nil { | ||||||
|  | 					mu.Lock() | ||||||
|  | 					successCount++ | ||||||
|  | 					mu.Unlock() | ||||||
|  |  | ||||||
|  | 					time.Sleep(100 * time.Millisecond) | ||||||
|  | 					suite.lockManager.ReleaseLock(ctx, lock) | ||||||
|  | 				} | ||||||
|  | 			}(i) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		wg.Wait() | ||||||
|  | 		assert.Equal(suite.T(), 1, successCount, "只有一个goroutine应该获得锁") | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestTransactionManager 测试事务管理器 | ||||||
|  | func (suite *OptimizedSystemTestSuite) TestTransactionManager() { | ||||||
|  | 	ctx := context.Background() | ||||||
|  |  | ||||||
|  | 	// 测试基本事务 | ||||||
|  | 	suite.Run("BasicTransaction", func() { | ||||||
|  | 		config := GetDefaultTransactionConfig() | ||||||
|  | 		result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error { | ||||||
|  | 			// 插入测试数据 | ||||||
|  | 			return txCtx.Tx.Exec(` | ||||||
|  | 				INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)  | ||||||
|  | 				VALUES (?, ?, ?, ?, ?, ?) | ||||||
|  | 			`, "order_001", "user_001", "BTCUSDT", "BUY", 1.0, "NEW").Error | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		assert.True(suite.T(), result.Success) | ||||||
|  | 		assert.NoError(suite.T(), result.Error) | ||||||
|  |  | ||||||
|  | 		// 验证数据已插入 | ||||||
|  | 		var count int64 | ||||||
|  | 		suite.db.Raw("SELECT COUNT(*) FROM test_orders WHERE order_id = ?", "order_001").Scan(&count) | ||||||
|  | 		assert.Equal(suite.T(), int64(1), count) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// 测试事务回滚 | ||||||
|  | 	suite.Run("TransactionRollback", func() { | ||||||
|  | 		config := GetDefaultTransactionConfig() | ||||||
|  | 		result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error { | ||||||
|  | 			// 插入数据 | ||||||
|  | 			if err := txCtx.Tx.Exec(` | ||||||
|  | 				INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)  | ||||||
|  | 				VALUES (?, ?, ?, ?, ?, ?) | ||||||
|  | 			`, "order_002", "user_002", "ETHUSDT", "SELL", 2.0, "NEW").Error; err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// 故意返回错误以触发回滚 | ||||||
|  | 			return fmt.Errorf("测试回滚") | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		assert.False(suite.T(), result.Success) | ||||||
|  | 		assert.Error(suite.T(), result.Error) | ||||||
|  |  | ||||||
|  | 		// 验证数据未插入 | ||||||
|  | 		var count int64 | ||||||
|  | 		suite.db.Raw("SELECT COUNT(*) FROM test_orders WHERE order_id = ?", "order_002").Scan(&count) | ||||||
|  | 		assert.Equal(suite.T(), int64(0), count) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// 测试乐观锁 | ||||||
|  | 	suite.Run("OptimisticLock", func() { | ||||||
|  | 		// 先插入测试数据 | ||||||
|  | 		suite.db.Exec(` | ||||||
|  | 			INSERT INTO test_positions (user_id, symbol, side, quantity, avg_price, status, version)  | ||||||
|  | 			VALUES (?, ?, ?, ?, ?, ?, ?) | ||||||
|  | 		`, "user_003", "BTCUSDT", "LONG", 1.0, 50000.0, "OPEN", 0) | ||||||
|  |  | ||||||
|  | 		// 测试正常更新 | ||||||
|  | 		updates := map[string]interface{}{ | ||||||
|  | 			"quantity": 2.0, | ||||||
|  | 		} | ||||||
|  | 		err := suite.txManager.UpdateWithOptimisticLock(ctx, nil, updates,  | ||||||
|  | 			"user_id = ? AND symbol = ? AND side = ? AND version = ?",  | ||||||
|  | 			"user_003", "BTCUSDT", "LONG", 0) | ||||||
|  | 		assert.NoError(suite.T(), err) | ||||||
|  |  | ||||||
|  | 		// 测试版本冲突 | ||||||
|  | 		updates["quantity"] = 3.0 | ||||||
|  | 		err = suite.txManager.UpdateWithOptimisticLock(ctx, nil, updates,  | ||||||
|  | 			"user_id = ? AND symbol = ? AND side = ? AND version = ?",  | ||||||
|  | 			"user_003", "BTCUSDT", "LONG", 0) // 使用旧版本号 | ||||||
|  | 		assert.Error(suite.T(), err) | ||||||
|  | 		assert.Contains(suite.T(), err.Error(), "乐观锁冲突") | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestErrorHandler 测试错误处理器 | ||||||
|  | func (suite *OptimizedSystemTestSuite) TestErrorHandler() { | ||||||
|  | 	ctx := context.Background() | ||||||
|  |  | ||||||
|  | 	// 测试错误分类 | ||||||
|  | 	suite.Run("ErrorClassification", func() { | ||||||
|  | 		testCases := []struct { | ||||||
|  | 			error    error | ||||||
|  | 			expected ErrorType | ||||||
|  | 		}{ | ||||||
|  | 			{fmt.Errorf("connection refused"), ErrorTypeNetwork}, | ||||||
|  | 			{fmt.Errorf("database connection failed"), ErrorTypeDatabase}, | ||||||
|  | 			{fmt.Errorf("context deadline exceeded"), ErrorTypeTimeout}, | ||||||
|  | 			{fmt.Errorf("too many requests"), ErrorTypeRateLimit}, | ||||||
|  | 			{fmt.Errorf("unauthorized access"), ErrorTypeAuth}, | ||||||
|  | 			{fmt.Errorf("invalid parameter"), ErrorTypeValidation}, | ||||||
|  | 			{fmt.Errorf("insufficient balance"), ErrorTypeBusiness}, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		for _, tc := range testCases { | ||||||
|  | 			errorInfo := suite.errorHandler.ClassifyError(tc.error, "test") | ||||||
|  | 			assert.Equal(suite.T(), tc.expected, errorInfo.Type) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// 测试重试机制 | ||||||
|  | 	suite.Run("RetryMechanism", func() { | ||||||
|  | 		attempts := 0 | ||||||
|  | 		config := RetryConfig{ | ||||||
|  | 			MaxRetries:     3, | ||||||
|  | 			RetryDelay:     10 * time.Millisecond, | ||||||
|  | 			MaxRetryDelay:  100 * time.Millisecond, | ||||||
|  | 			BackoffFactor:  2.0, | ||||||
|  | 			ApiRetryCount:  3, | ||||||
|  | 			DbRetryCount:   3, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 测试重试成功 | ||||||
|  | 		err := suite.errorHandler.RetryWithBackoff(ctx, func() error { | ||||||
|  | 			attempts++ | ||||||
|  | 			if attempts < 3 { | ||||||
|  | 				return fmt.Errorf("temporary error") | ||||||
|  | 			} | ||||||
|  | 			return nil | ||||||
|  | 		}, config, "test_retry") | ||||||
|  |  | ||||||
|  | 		assert.NoError(suite.T(), err) | ||||||
|  | 		assert.Equal(suite.T(), 3, attempts) | ||||||
|  |  | ||||||
|  | 		// 测试重试失败 | ||||||
|  | 		attempts = 0 | ||||||
|  | 		err = suite.errorHandler.RetryWithBackoff(ctx, func() error { | ||||||
|  | 			attempts++ | ||||||
|  | 			return fmt.Errorf("persistent error") | ||||||
|  | 		}, config, "test_retry_fail") | ||||||
|  |  | ||||||
|  | 		assert.Error(suite.T(), err) | ||||||
|  | 		assert.Equal(suite.T(), 3, attempts) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestMetricsCollector 测试指标收集器 | ||||||
|  | func (suite *OptimizedSystemTestSuite) TestMetricsCollector() { | ||||||
|  | 	// 测试订单指标 | ||||||
|  | 	suite.Run("OrderMetrics", func() { | ||||||
|  | 		// 记录订单处理 | ||||||
|  | 		suite.metricsCollector.RecordOrderProcessed(true, 100*time.Millisecond) | ||||||
|  | 		suite.metricsCollector.RecordOrderProcessed(false, 200*time.Millisecond) | ||||||
|  | 		suite.metricsCollector.RecordOrderCanceled() | ||||||
|  | 		suite.metricsCollector.RecordReverseOrder(true) | ||||||
|  |  | ||||||
|  | 		metrics := suite.metricsCollector.GetMetrics() | ||||||
|  | 		orderMetrics := metrics["order_metrics"].(*OrderMetrics) | ||||||
|  |  | ||||||
|  | 		assert.Equal(suite.T(), int64(2), orderMetrics.TotalProcessed) | ||||||
|  | 		assert.Equal(suite.T(), int64(1), orderMetrics.SuccessfulOrders) | ||||||
|  | 		assert.Equal(suite.T(), int64(1), orderMetrics.FailedOrders) | ||||||
|  | 		assert.Equal(suite.T(), int64(1), orderMetrics.CanceledOrders) | ||||||
|  | 		assert.Equal(suite.T(), int64(1), orderMetrics.ReverseOrdersCreated) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// 测试持仓指标 | ||||||
|  | 	suite.Run("PositionMetrics", func() { | ||||||
|  | 		difference := decimal.NewFromFloat(0.1) | ||||||
|  | 		suite.metricsCollector.RecordPositionSync(true, difference) | ||||||
|  | 		suite.metricsCollector.RecordPositionSync(false, decimal.Zero) | ||||||
|  | 		suite.metricsCollector.RecordClosePosition(false) | ||||||
|  | 		suite.metricsCollector.RecordClosePosition(true) | ||||||
|  |  | ||||||
|  | 		metrics := suite.metricsCollector.GetMetrics() | ||||||
|  | 		positionMetrics := metrics["position_metrics"].(*PositionMetrics) | ||||||
|  |  | ||||||
|  | 		assert.Equal(suite.T(), int64(2), positionMetrics.SyncAttempts) | ||||||
|  | 		assert.Equal(suite.T(), int64(1), positionMetrics.SyncSuccesses) | ||||||
|  | 		assert.Equal(suite.T(), int64(1), positionMetrics.SyncFailures) | ||||||
|  | 		assert.Equal(suite.T(), int64(2), positionMetrics.ClosePositions) | ||||||
|  | 		assert.Equal(suite.T(), int64(1), positionMetrics.ForceClosePositions) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// 测试性能指标 | ||||||
|  | 	suite.Run("PerformanceMetrics", func() { | ||||||
|  | 		suite.metricsCollector.RecordLockOperation(true, 50*time.Millisecond) | ||||||
|  | 		suite.metricsCollector.RecordDbOperation(false, 30*time.Millisecond, nil) | ||||||
|  | 		suite.metricsCollector.RecordApiCall(100*time.Millisecond, nil, 1) | ||||||
|  | 		suite.metricsCollector.RecordConcurrency(5) | ||||||
|  |  | ||||||
|  | 		metrics := suite.metricsCollector.GetMetrics() | ||||||
|  | 		perfMetrics := metrics["performance_metrics"].(*PerformanceMetrics) | ||||||
|  |  | ||||||
|  | 		assert.Equal(suite.T(), int64(1), perfMetrics.LockAcquisitions) | ||||||
|  | 		assert.Equal(suite.T(), int64(1), perfMetrics.DbQueries) | ||||||
|  | 		assert.Equal(suite.T(), int64(1), perfMetrics.ApiCalls) | ||||||
|  | 		assert.Equal(suite.T(), int64(5), perfMetrics.ConcurrentRequests) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestConcurrentOperations 测试并发操作 | ||||||
|  | func (suite *OptimizedSystemTestSuite) TestConcurrentOperations() { | ||||||
|  | 	ctx := context.Background() | ||||||
|  |  | ||||||
|  | 	suite.Run("ConcurrentOrderProcessing", func() { | ||||||
|  | 		var wg sync.WaitGroup | ||||||
|  | 		successCount := 0 | ||||||
|  | 		var mu sync.Mutex | ||||||
|  |  | ||||||
|  | 		// 并发处理订单 | ||||||
|  | 		for i := 0; i < 10; i++ { | ||||||
|  | 			wg.Add(1) | ||||||
|  | 			go func(id int) { | ||||||
|  | 				defer wg.Done() | ||||||
|  |  | ||||||
|  | 				// 使用锁保护订单处理 | ||||||
|  | 				lockConfig := GetOrderLockConfig(fmt.Sprintf("order_%d", id)) | ||||||
|  | 				err := suite.lockManager.WithLock(ctx, lockConfig, func() error { | ||||||
|  | 					// 模拟订单处理 | ||||||
|  | 					config := GetDefaultTransactionConfig() | ||||||
|  | 					result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error { | ||||||
|  | 						return txCtx.Tx.Exec(` | ||||||
|  | 							INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)  | ||||||
|  | 							VALUES (?, ?, ?, ?, ?, ?) | ||||||
|  | 						`, fmt.Sprintf("concurrent_order_%d", id), fmt.Sprintf("user_%d", id),  | ||||||
|  | 							"BTCUSDT", "BUY", 1.0, "NEW").Error | ||||||
|  | 					}) | ||||||
|  |  | ||||||
|  | 					if result.Success { | ||||||
|  | 						mu.Lock() | ||||||
|  | 						successCount++ | ||||||
|  | 						mu.Unlock() | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					return result.Error | ||||||
|  | 				}) | ||||||
|  |  | ||||||
|  | 				if err != nil { | ||||||
|  | 					logger.Errorf("并发订单处理失败 [%d]: %v", id, err) | ||||||
|  | 				} | ||||||
|  | 			}(i) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		wg.Wait() | ||||||
|  | 		assert.Equal(suite.T(), 10, successCount, "所有并发订单都应该成功处理") | ||||||
|  |  | ||||||
|  | 		// 验证数据库中的记录数 | ||||||
|  | 		var count int64 | ||||||
|  | 		suite.db.Raw("SELECT COUNT(*) FROM test_orders WHERE order_id LIKE 'concurrent_order_%'").Scan(&count) | ||||||
|  | 		assert.Equal(suite.T(), int64(10), count) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestSystemIntegration 测试系统集成 | ||||||
|  | func (suite *OptimizedSystemTestSuite) TestSystemIntegration() { | ||||||
|  | 	ctx := context.Background() | ||||||
|  |  | ||||||
|  | 	suite.Run("CompleteOrderFlow", func() { | ||||||
|  | 		userID := "integration_user" | ||||||
|  | 		symbol := "BTCUSDT" | ||||||
|  | 		orderID := "integration_order_001" | ||||||
|  |  | ||||||
|  | 		// 步骤1:创建订单(使用锁和事务) | ||||||
|  | 		err := WithOrderLock(ctx, orderID, func() error { | ||||||
|  | 			return WithDefaultTransaction(ctx, func(txCtx *TransactionContext) error { | ||||||
|  | 				return txCtx.Tx.Exec(` | ||||||
|  | 					INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, price, status)  | ||||||
|  | 					VALUES (?, ?, ?, ?, ?, ?, ?) | ||||||
|  | 				`, orderID, userID, symbol, "BUY", 1.0, 50000.0, "NEW").Error | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 		assert.NoError(suite.T(), err) | ||||||
|  |  | ||||||
|  | 		// 步骤2:更新订单状态(使用乐观锁) | ||||||
|  | 		updates := map[string]interface{}{ | ||||||
|  | 			"status": "FILLED", | ||||||
|  | 		} | ||||||
|  | 		err = UpdateWithOptimisticLockGlobal(ctx, nil, updates,  | ||||||
|  | 			"order_id = ? AND version = ?", orderID, 0) | ||||||
|  | 		assert.NoError(suite.T(), err) | ||||||
|  |  | ||||||
|  | 		// 步骤3:创建持仓(使用锁和事务) | ||||||
|  | 		err = WithPositionLock(ctx, userID, symbol, func() error { | ||||||
|  | 			return WithDefaultTransaction(ctx, func(txCtx *TransactionContext) error { | ||||||
|  | 				return txCtx.Tx.Exec(` | ||||||
|  | 					INSERT INTO test_positions (user_id, symbol, side, quantity, avg_price, status)  | ||||||
|  | 					VALUES (?, ?, ?, ?, ?, ?) | ||||||
|  | 					ON DUPLICATE KEY UPDATE  | ||||||
|  | 					quantity = quantity + VALUES(quantity), | ||||||
|  | 					avg_price = (avg_price * quantity + VALUES(avg_price) * VALUES(quantity)) / (quantity + VALUES(quantity)), | ||||||
|  | 					version = version + 1 | ||||||
|  | 				`, userID, symbol, "LONG", 1.0, 50000.0, "OPEN").Error | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 		assert.NoError(suite.T(), err) | ||||||
|  |  | ||||||
|  | 		// 验证最终状态 | ||||||
|  | 		var orderStatus string | ||||||
|  | 		suite.db.Raw("SELECT status FROM test_orders WHERE order_id = ?", orderID).Scan(&orderStatus) | ||||||
|  | 		assert.Equal(suite.T(), "FILLED", orderStatus) | ||||||
|  |  | ||||||
|  | 		var positionQuantity float64 | ||||||
|  | 		suite.db.Raw("SELECT quantity FROM test_positions WHERE user_id = ? AND symbol = ?",  | ||||||
|  | 			userID, symbol).Scan(&positionQuantity) | ||||||
|  | 		assert.Equal(suite.T(), 1.0, positionQuantity) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestPerformance 测试性能 | ||||||
|  | func (suite *OptimizedSystemTestSuite) TestPerformance() { | ||||||
|  | 	ctx := context.Background() | ||||||
|  |  | ||||||
|  | 	suite.Run("LockPerformance", func() { | ||||||
|  | 		startTime := time.Now() | ||||||
|  | 		iterations := 1000 | ||||||
|  |  | ||||||
|  | 		for i := 0; i < iterations; i++ { | ||||||
|  | 			lockConfig := GetOrderLockConfig(fmt.Sprintf("perf_order_%d", i)) | ||||||
|  | 			lock, err := suite.lockManager.AcquireLock(ctx, lockConfig) | ||||||
|  | 			assert.NoError(suite.T(), err) | ||||||
|  | 			suite.lockManager.ReleaseLock(ctx, lock) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		duration := time.Since(startTime) | ||||||
|  | 		avgDuration := duration / time.Duration(iterations) | ||||||
|  | 		logger.Infof("锁性能测试: %d 次操作耗时 %v,平均每次 %v", iterations, duration, avgDuration) | ||||||
|  |  | ||||||
|  | 		// 平均每次操作应该在合理范围内 | ||||||
|  | 		assert.Less(suite.T(), avgDuration, 10*time.Millisecond) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	suite.Run("TransactionPerformance", func() { | ||||||
|  | 		startTime := time.Now() | ||||||
|  | 		iterations := 100 | ||||||
|  |  | ||||||
|  | 		for i := 0; i < iterations; i++ { | ||||||
|  | 			config := GetDefaultTransactionConfig() | ||||||
|  | 			result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error { | ||||||
|  | 				return txCtx.Tx.Exec(` | ||||||
|  | 					INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)  | ||||||
|  | 					VALUES (?, ?, ?, ?, ?, ?) | ||||||
|  | 				`, fmt.Sprintf("perf_order_%d", i), "perf_user", "BTCUSDT", "BUY", 1.0, "NEW").Error | ||||||
|  | 			}) | ||||||
|  | 			assert.True(suite.T(), result.Success) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		duration := time.Since(startTime) | ||||||
|  | 		avgDuration := duration / time.Duration(iterations) | ||||||
|  | 		logger.Infof("事务性能测试: %d 次操作耗时 %v,平均每次 %v", iterations, duration, avgDuration) | ||||||
|  |  | ||||||
|  | 		// 平均每次事务应该在合理范围内 | ||||||
|  | 		assert.Less(suite.T(), avgDuration, 100*time.Millisecond) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestSuite 运行测试套件 | ||||||
|  | func TestOptimizedSystemSuite(t *testing.T) { | ||||||
|  | 	suite.Run(t, new(OptimizedSystemTestSuite)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BenchmarkLockManager 锁管理器基准测试 | ||||||
|  | func BenchmarkLockManager(b *testing.B) { | ||||||
|  | 	// 这里可以添加基准测试 | ||||||
|  | 	b.Skip("基准测试需要Redis连接") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BenchmarkTransactionManager 事务管理器基准测试 | ||||||
|  | func BenchmarkTransactionManager(b *testing.B) { | ||||||
|  | 	// 这里可以添加基准测试 | ||||||
|  | 	b.Skip("基准测试需要数据库连接") | ||||||
|  | } | ||||||
							
								
								
									
										575
									
								
								services/binanceservice/lock_manager_optimized.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										575
									
								
								services/binanceservice/lock_manager_optimized.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,575 @@ | |||||||
|  | package binanceservice | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/logger" | ||||||
|  | 	"github.com/go-redis/redis/v8" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // LockManagerConfig 锁管理器专用配置 | ||||||
|  | type LockManagerConfig struct { | ||||||
|  | 	Type       LockType      `json:"type"` | ||||||
|  | 	Scope      LockScope     `json:"scope"` | ||||||
|  | 	Key        string        `json:"key"` | ||||||
|  | 	Expiration time.Duration `json:"expiration"` | ||||||
|  | 	Timeout    time.Duration `json:"timeout"` | ||||||
|  | 	RetryDelay time.Duration `json:"retry_delay"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LockType 锁类型 | ||||||
|  | type LockType string | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	LockTypeLocal       LockType = "local" | ||||||
|  | 	LockTypeDistributed LockType = "distributed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // LockScope 锁范围 | ||||||
|  | type LockScope string | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	ScopeOrder    LockScope = "order" | ||||||
|  | 	ScopePosition LockScope = "position" | ||||||
|  | 	ScopeUser     LockScope = "user" | ||||||
|  | 	ScopeSymbol   LockScope = "symbol" | ||||||
|  | 	ScopeGlobal   LockScope = "global" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Lock 锁接口 | ||||||
|  | type Lock interface { | ||||||
|  | 	Lock(ctx context.Context) error | ||||||
|  | 	Unlock(ctx context.Context) error | ||||||
|  | 	TryLock(ctx context.Context) (bool, error) | ||||||
|  | 	IsLocked() bool | ||||||
|  | 	GetKey() string | ||||||
|  | 	GetExpiration() time.Duration | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LocalLock 本地锁 | ||||||
|  | type LocalLock struct { | ||||||
|  | 	key        string | ||||||
|  | 	mu         *sync.RWMutex | ||||||
|  | 	locked     bool | ||||||
|  | 	expiration time.Duration | ||||||
|  | 	acquiredAt time.Time | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DistributedLock 分布式锁 | ||||||
|  | type DistributedLock struct { | ||||||
|  | 	key        string | ||||||
|  | 	value      string | ||||||
|  | 	expiration time.Duration | ||||||
|  | 	redisClient *redis.Client | ||||||
|  | 	locked     bool | ||||||
|  | 	acquiredAt time.Time | ||||||
|  | 	refreshTicker *time.Ticker | ||||||
|  | 	stopRefresh   chan struct{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LockManager 锁管理器 | ||||||
|  | type LockManager struct { | ||||||
|  | 	config       *OptimizedConfig | ||||||
|  | 	redisClient  *redis.Client | ||||||
|  | 	localLocks   map[string]*LocalLock | ||||||
|  | 	localMutex   sync.RWMutex | ||||||
|  | 	metrics      *MetricsCollector | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LockConfig 已在 config_optimized.go 中定义 | ||||||
|  |  | ||||||
|  | // NewLockManager 创建锁管理器 | ||||||
|  | func NewLockManager(config *OptimizedConfig, redisClient *redis.Client, metrics *MetricsCollector) *LockManager { | ||||||
|  | 	return &LockManager{ | ||||||
|  | 		config:      config, | ||||||
|  | 		redisClient: redisClient, | ||||||
|  | 		localLocks:  make(map[string]*LocalLock), | ||||||
|  | 		metrics:     metrics, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateLock 创建锁 | ||||||
|  | func (lm *LockManager) CreateLock(config LockManagerConfig) Lock { | ||||||
|  | 	switch config.Type { | ||||||
|  | 	case LockTypeDistributed: | ||||||
|  | 		return lm.createDistributedLock(config) | ||||||
|  | 	case LockTypeLocal: | ||||||
|  | 		return lm.createLocalLock(config) | ||||||
|  | 	default: | ||||||
|  | 		// 默认使用分布式锁 | ||||||
|  | 		return lm.createDistributedLock(config) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // createLocalLock 创建本地锁 | ||||||
|  | func (lm *LockManager) createLocalLock(config LockManagerConfig) *LocalLock { | ||||||
|  | 	lm.localMutex.Lock() | ||||||
|  | 	defer lm.localMutex.Unlock() | ||||||
|  |  | ||||||
|  | 	key := lm.buildLockKey(config.Scope, config.Key) | ||||||
|  | 	if lock, exists := lm.localLocks[key]; exists { | ||||||
|  | 		return lock | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	lock := &LocalLock{ | ||||||
|  | 		key:        key, | ||||||
|  | 		mu:         &sync.RWMutex{}, | ||||||
|  | 		expiration: config.Expiration, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	lm.localLocks[key] = lock | ||||||
|  | 	return lock | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // createDistributedLock 创建分布式锁 | ||||||
|  | func (lm *LockManager) createDistributedLock(config LockManagerConfig) *DistributedLock { | ||||||
|  | 	key := lm.buildLockKey(config.Scope, config.Key) | ||||||
|  | 	value := fmt.Sprintf("%d_%s", time.Now().UnixNano(), generateRandomString(8)) | ||||||
|  |  | ||||||
|  | 	return &DistributedLock{ | ||||||
|  | 		key:         key, | ||||||
|  | 		value:       value, | ||||||
|  | 		expiration:  config.Expiration, | ||||||
|  | 		redisClient: lm.redisClient, | ||||||
|  | 		stopRefresh: make(chan struct{}), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // buildLockKey 构建锁键名 | ||||||
|  | func (lm *LockManager) buildLockKey(scope LockScope, key string) string { | ||||||
|  | 	return fmt.Sprintf("lock:%s:%s", scope, key) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AcquireLock 获取锁(带超时和重试) | ||||||
|  | func (lm *LockManager) AcquireLock(ctx context.Context, config LockManagerConfig) (Lock, error) { | ||||||
|  | 	lock := lm.CreateLock(config) | ||||||
|  | 	startTime := time.Now() | ||||||
|  |  | ||||||
|  | 	// 设置超时上下文 | ||||||
|  | 	lockCtx := ctx | ||||||
|  | 	if config.Timeout > 0 { | ||||||
|  | 		var cancel context.CancelFunc | ||||||
|  | 		lockCtx, cancel = context.WithTimeout(ctx, config.Timeout) | ||||||
|  | 		defer cancel() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 重试获取锁 | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case <-lockCtx.Done(): | ||||||
|  | 			waitTime := time.Since(startTime) | ||||||
|  | 			if lm.metrics != nil { | ||||||
|  | 				lm.metrics.RecordLockOperation(false, waitTime) | ||||||
|  | 			} | ||||||
|  | 			return nil, fmt.Errorf("获取锁超时: %s", config.Key) | ||||||
|  |  | ||||||
|  | 		default: | ||||||
|  | 			err := lock.Lock(lockCtx) | ||||||
|  | 			if err == nil { | ||||||
|  | 				waitTime := time.Since(startTime) | ||||||
|  | 				if lm.metrics != nil { | ||||||
|  | 					lm.metrics.RecordLockOperation(true, waitTime) | ||||||
|  | 				} | ||||||
|  | 				return lock, nil | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// 等待重试 | ||||||
|  | 			select { | ||||||
|  | 			case <-lockCtx.Done(): | ||||||
|  | 				return nil, lockCtx.Err() | ||||||
|  | 			case <-time.After(config.RetryDelay): | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ReleaseLock 释放锁 | ||||||
|  | func (lm *LockManager) ReleaseLock(ctx context.Context, lock Lock) error { | ||||||
|  | 	if lock == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return lock.Unlock(ctx) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithLock 使用锁执行操作 | ||||||
|  | func (lm *LockManager) WithLock(ctx context.Context, config LockManagerConfig, operation func() error) error { | ||||||
|  | 	lock, err := lm.AcquireLock(ctx, config) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("获取锁失败: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	defer func() { | ||||||
|  | 		if unlockErr := lm.ReleaseLock(ctx, lock); unlockErr != nil { | ||||||
|  | 			logger.Errorf("释放锁失败 [%s]: %v", config.Key, unlockErr) | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	return operation() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CleanupExpiredLocks 清理过期的本地锁 | ||||||
|  | func (lm *LockManager) CleanupExpiredLocks() { | ||||||
|  | 	lm.localMutex.Lock() | ||||||
|  | 	defer lm.localMutex.Unlock() | ||||||
|  |  | ||||||
|  | 	now := time.Now() | ||||||
|  | 	for key, lock := range lm.localLocks { | ||||||
|  | 		if lock.locked && lock.expiration > 0 && now.Sub(lock.acquiredAt) > lock.expiration { | ||||||
|  | 			lock.locked = false | ||||||
|  | 			logger.Warnf("本地锁已过期并被清理: %s", key) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetLockStatus 获取锁状态 | ||||||
|  | func (lm *LockManager) GetLockStatus() map[string]interface{} { | ||||||
|  | 	lm.localMutex.RLock() | ||||||
|  | 	defer lm.localMutex.RUnlock() | ||||||
|  |  | ||||||
|  | 	status := make(map[string]interface{}) | ||||||
|  | 	localLocks := make(map[string]interface{}) | ||||||
|  |  | ||||||
|  | 	for key, lock := range lm.localLocks { | ||||||
|  | 		localLocks[key] = map[string]interface{}{ | ||||||
|  | 			"locked":      lock.locked, | ||||||
|  | 			"acquired_at": lock.acquiredAt, | ||||||
|  | 			"expiration":  lock.expiration, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	status["local_locks"] = localLocks | ||||||
|  | 	status["total_local_locks"] = len(lm.localLocks) | ||||||
|  |  | ||||||
|  | 	return status | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LocalLock 实现 | ||||||
|  |  | ||||||
|  | // Lock 获取本地锁 | ||||||
|  | func (ll *LocalLock) Lock(ctx context.Context) error { | ||||||
|  | 	ll.mu.Lock() | ||||||
|  | 	defer ll.mu.Unlock() | ||||||
|  |  | ||||||
|  | 	if ll.locked { | ||||||
|  | 		return fmt.Errorf("锁已被占用: %s", ll.key) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ll.locked = true | ||||||
|  | 	ll.acquiredAt = time.Now() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Unlock 释放本地锁 | ||||||
|  | func (ll *LocalLock) Unlock(ctx context.Context) error { | ||||||
|  | 	ll.mu.Lock() | ||||||
|  | 	defer ll.mu.Unlock() | ||||||
|  |  | ||||||
|  | 	if !ll.locked { | ||||||
|  | 		return fmt.Errorf("锁未被占用: %s", ll.key) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ll.locked = false | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TryLock 尝试获取本地锁 | ||||||
|  | func (ll *LocalLock) TryLock(ctx context.Context) (bool, error) { | ||||||
|  | 	ll.mu.Lock() | ||||||
|  | 	defer ll.mu.Unlock() | ||||||
|  |  | ||||||
|  | 	if ll.locked { | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ll.locked = true | ||||||
|  | 	ll.acquiredAt = time.Now() | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsLocked 检查本地锁是否被占用 | ||||||
|  | func (ll *LocalLock) IsLocked() bool { | ||||||
|  | 	ll.mu.RLock() | ||||||
|  | 	defer ll.mu.RUnlock() | ||||||
|  | 	return ll.locked | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetKey 获取锁键名 | ||||||
|  | func (ll *LocalLock) GetKey() string { | ||||||
|  | 	return ll.key | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetExpiration 获取过期时间 | ||||||
|  | func (ll *LocalLock) GetExpiration() time.Duration { | ||||||
|  | 	return ll.expiration | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DistributedLock 实现 | ||||||
|  |  | ||||||
|  | // Lock 获取分布式锁 | ||||||
|  | func (dl *DistributedLock) Lock(ctx context.Context) error { | ||||||
|  | 	// 使用 SET key value EX seconds NX 命令 | ||||||
|  | 	result := dl.redisClient.SetNX(ctx, dl.key, dl.value, dl.expiration) | ||||||
|  | 	if result.Err() != nil { | ||||||
|  | 		return fmt.Errorf("获取分布式锁失败: %w", result.Err()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !result.Val() { | ||||||
|  | 		return fmt.Errorf("分布式锁已被占用: %s", dl.key) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dl.locked = true | ||||||
|  | 	dl.acquiredAt = time.Now() | ||||||
|  |  | ||||||
|  | 	// 启动锁续期 | ||||||
|  | 	dl.startRefresh() | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Unlock 释放分布式锁 | ||||||
|  | func (dl *DistributedLock) Unlock(ctx context.Context) error { | ||||||
|  | 	if !dl.locked { | ||||||
|  | 		return fmt.Errorf("分布式锁未被占用: %s", dl.key) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 停止续期 | ||||||
|  | 	dl.stopRefreshProcess() | ||||||
|  |  | ||||||
|  | 	// 使用 Lua 脚本确保只删除自己的锁 | ||||||
|  | 	luaScript := ` | ||||||
|  | 		if redis.call("get", KEYS[1]) == ARGV[1] then | ||||||
|  | 			return redis.call("del", KEYS[1]) | ||||||
|  | 		else | ||||||
|  | 			return 0 | ||||||
|  | 		end | ||||||
|  | 	` | ||||||
|  |  | ||||||
|  | 	result := dl.redisClient.Eval(ctx, luaScript, []string{dl.key}, dl.value) | ||||||
|  | 	if result.Err() != nil { | ||||||
|  | 		return fmt.Errorf("释放分布式锁失败: %w", result.Err()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dl.locked = false | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TryLock 尝试获取分布式锁 | ||||||
|  | func (dl *DistributedLock) TryLock(ctx context.Context) (bool, error) { | ||||||
|  | 	result := dl.redisClient.SetNX(ctx, dl.key, dl.value, dl.expiration) | ||||||
|  | 	if result.Err() != nil { | ||||||
|  | 		return false, fmt.Errorf("尝试获取分布式锁失败: %w", result.Err()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if result.Val() { | ||||||
|  | 		dl.locked = true | ||||||
|  | 		dl.acquiredAt = time.Now() | ||||||
|  | 		dl.startRefresh() | ||||||
|  | 		return true, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsLocked 检查分布式锁是否被占用 | ||||||
|  | func (dl *DistributedLock) IsLocked() bool { | ||||||
|  | 	return dl.locked | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetKey 获取锁键名 | ||||||
|  | func (dl *DistributedLock) GetKey() string { | ||||||
|  | 	return dl.key | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetExpiration 获取过期时间 | ||||||
|  | func (dl *DistributedLock) GetExpiration() time.Duration { | ||||||
|  | 	return dl.expiration | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // startRefresh 启动锁续期 | ||||||
|  | func (dl *DistributedLock) startRefresh() { | ||||||
|  | 	if dl.expiration <= 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 每隔过期时间的1/3进行续期 | ||||||
|  | 	refreshInterval := dl.expiration / 3 | ||||||
|  | 	dl.refreshTicker = time.NewTicker(refreshInterval) | ||||||
|  |  | ||||||
|  | 	go func() { | ||||||
|  | 		defer dl.refreshTicker.Stop() | ||||||
|  |  | ||||||
|  | 		for { | ||||||
|  | 			select { | ||||||
|  | 			case <-dl.stopRefresh: | ||||||
|  | 				return | ||||||
|  | 			case <-dl.refreshTicker.C: | ||||||
|  | 				if !dl.locked { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				// 使用 Lua 脚本续期 | ||||||
|  | 				luaScript := ` | ||||||
|  | 					if redis.call("get", KEYS[1]) == ARGV[1] then | ||||||
|  | 						return redis.call("expire", KEYS[1], ARGV[2]) | ||||||
|  | 					else | ||||||
|  | 						return 0 | ||||||
|  | 					end | ||||||
|  | 				` | ||||||
|  |  | ||||||
|  | 				ctx := context.Background() | ||||||
|  | 				result := dl.redisClient.Eval(ctx, luaScript, []string{dl.key}, dl.value, int(dl.expiration.Seconds())) | ||||||
|  | 				if result.Err() != nil { | ||||||
|  | 					logger.Errorf("分布式锁续期失败 [%s]: %v", dl.key, result.Err()) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				if result.Val().(int64) == 0 { | ||||||
|  | 					logger.Warnf("分布式锁已被其他进程占用,停止续期 [%s]", dl.key) | ||||||
|  | 					dl.locked = false | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // stopRefreshProcess 停止续期进程 | ||||||
|  | func (dl *DistributedLock) stopRefreshProcess() { | ||||||
|  | 	if dl.refreshTicker != nil { | ||||||
|  | 		close(dl.stopRefresh) | ||||||
|  | 		dl.refreshTicker.Stop() | ||||||
|  | 		dl.refreshTicker = nil | ||||||
|  | 		dl.stopRefresh = make(chan struct{}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 辅助函数 | ||||||
|  |  | ||||||
|  | // generateRandomString 生成随机字符串 | ||||||
|  | func generateRandomString(length int) string { | ||||||
|  | 	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" | ||||||
|  | 	b := make([]byte, length) | ||||||
|  | 	for i := range b { | ||||||
|  | 		b[i] = charset[time.Now().UnixNano()%int64(len(charset))] | ||||||
|  | 	} | ||||||
|  | 	return string(b) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 预定义的锁配置 | ||||||
|  |  | ||||||
|  | // GetOrderLockConfig 获取订单锁配置 | ||||||
|  | func GetOrderLockConfig(orderID string) LockManagerConfig { | ||||||
|  | 	return LockManagerConfig{ | ||||||
|  | 		Type:       LockTypeDistributed, | ||||||
|  | 		Scope:      ScopeOrder, | ||||||
|  | 		Key:        orderID, | ||||||
|  | 		Expiration: 30 * time.Second, | ||||||
|  | 		Timeout:    5 * time.Second, | ||||||
|  | 		RetryDelay: 100 * time.Millisecond, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetPositionLockConfig 获取持仓锁配置 | ||||||
|  | func GetPositionLockConfig(userID, symbol string) LockManagerConfig { | ||||||
|  | 	return LockManagerConfig{ | ||||||
|  | 		Type:       LockTypeDistributed, | ||||||
|  | 		Scope:      ScopePosition, | ||||||
|  | 		Key:        fmt.Sprintf("%s_%s", userID, symbol), | ||||||
|  | 		Expiration: 60 * time.Second, | ||||||
|  | 		Timeout:    10 * time.Second, | ||||||
|  | 		RetryDelay: 200 * time.Millisecond, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetUserLockConfig 获取用户锁配置 | ||||||
|  | func GetUserLockConfig(userID string) LockManagerConfig { | ||||||
|  | 	return LockManagerConfig{ | ||||||
|  | 		Type:       LockTypeLocal, | ||||||
|  | 		Scope:      ScopeUser, | ||||||
|  | 		Key:        userID, | ||||||
|  | 		Expiration: 30 * time.Second, | ||||||
|  | 		Timeout:    3 * time.Second, | ||||||
|  | 		RetryDelay: 50 * time.Millisecond, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetSymbolLockConfig 获取交易对锁配置 | ||||||
|  | func GetSymbolLockConfig(symbol string) LockManagerConfig { | ||||||
|  | 	return LockManagerConfig{ | ||||||
|  | 		Type:       LockTypeLocal, | ||||||
|  | 		Scope:      ScopeSymbol, | ||||||
|  | 		Key:        symbol, | ||||||
|  | 		Expiration: 15 * time.Second, | ||||||
|  | 		Timeout:    2 * time.Second, | ||||||
|  | 		RetryDelay: 25 * time.Millisecond, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetGlobalLockConfig 获取全局锁配置 | ||||||
|  | func GetGlobalLockConfig(operation string) LockManagerConfig { | ||||||
|  | 	return LockManagerConfig{ | ||||||
|  | 		Type:       LockTypeDistributed, | ||||||
|  | 		Scope:      ScopeGlobal, | ||||||
|  | 		Key:        operation, | ||||||
|  | 		Expiration: 120 * time.Second, | ||||||
|  | 		Timeout:    30 * time.Second, | ||||||
|  | 		RetryDelay: 500 * time.Millisecond, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 全局锁管理器实例 | ||||||
|  | var GlobalLockManager *LockManager | ||||||
|  |  | ||||||
|  | // InitLockManager 初始化锁管理器 | ||||||
|  | func InitLockManager(config *OptimizedConfig, redisClient *redis.Client, metrics *MetricsCollector) { | ||||||
|  | 	GlobalLockManager = NewLockManager(config, redisClient, metrics) | ||||||
|  |  | ||||||
|  | 	// 启动清理过期锁的定时任务 | ||||||
|  | 	go func() { | ||||||
|  | 		ticker := time.NewTicker(time.Minute) | ||||||
|  | 		defer ticker.Stop() | ||||||
|  |  | ||||||
|  | 		for range ticker.C { | ||||||
|  | 			GlobalLockManager.CleanupExpiredLocks() | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetLockManager 获取全局锁管理器 | ||||||
|  | func GetLockManager() *LockManager { | ||||||
|  | 	return GlobalLockManager | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithOrderLock 使用订单锁执行操作 | ||||||
|  | func WithOrderLock(ctx context.Context, orderID string, operation func() error) error { | ||||||
|  | 	if GlobalLockManager == nil { | ||||||
|  | 		return operation() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	config := GetOrderLockConfig(orderID) | ||||||
|  | 	return GlobalLockManager.WithLock(ctx, config, operation) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithPositionLock 使用持仓锁执行操作 | ||||||
|  | func WithPositionLock(ctx context.Context, userID, symbol string, operation func() error) error { | ||||||
|  | 	if GlobalLockManager == nil { | ||||||
|  | 		return operation() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	config := GetPositionLockConfig(userID, symbol) | ||||||
|  | 	return GlobalLockManager.WithLock(ctx, config, operation) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithUserLock 使用用户锁执行操作 | ||||||
|  | func WithUserLock(ctx context.Context, userID string, operation func() error) error { | ||||||
|  | 	if GlobalLockManager == nil { | ||||||
|  | 		return operation() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	config := GetUserLockConfig(userID) | ||||||
|  | 	return GlobalLockManager.WithLock(ctx, config, operation) | ||||||
|  | } | ||||||
							
								
								
									
										594
									
								
								services/binanceservice/monitor_optimized.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										594
									
								
								services/binanceservice/monitor_optimized.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,594 @@ | |||||||
|  | package binanceservice | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"runtime" | ||||||
|  | 	"sync" | ||||||
|  | 	"sync/atomic" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/logger" | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // MetricsCollector 指标收集器 | ||||||
|  | type MetricsCollector struct { | ||||||
|  | 	mu sync.RWMutex | ||||||
|  | 	 | ||||||
|  | 	// 业务指标 | ||||||
|  | 	OrderMetrics      *OrderMetrics | ||||||
|  | 	PositionMetrics   *PositionMetrics | ||||||
|  | 	TakeProfitMetrics *TakeProfitMetrics | ||||||
|  | 	 | ||||||
|  | 	// 技术指标 | ||||||
|  | 	PerformanceMetrics *PerformanceMetrics | ||||||
|  | 	ErrorMetrics       *ErrorMetrics | ||||||
|  | 	 | ||||||
|  | 	// 系统指标 | ||||||
|  | 	SystemMetrics *SystemMetrics | ||||||
|  | 	 | ||||||
|  | 	// 配置 | ||||||
|  | 	config *OptimizedConfig | ||||||
|  | 	 | ||||||
|  | 	// 控制 | ||||||
|  | 	ctx    context.Context | ||||||
|  | 	cancel context.CancelFunc | ||||||
|  | 	startTime time.Time | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // OrderMetrics 订单相关指标 | ||||||
|  | type OrderMetrics struct { | ||||||
|  | 	// 订单处理统计 | ||||||
|  | 	TotalProcessed    int64 `json:"total_processed"` | ||||||
|  | 	SuccessfulOrders  int64 `json:"successful_orders"` | ||||||
|  | 	FailedOrders      int64 `json:"failed_orders"` | ||||||
|  | 	CanceledOrders    int64 `json:"canceled_orders"` | ||||||
|  | 	 | ||||||
|  | 	// 反向订单统计 | ||||||
|  | 	ReverseOrdersCreated int64 `json:"reverse_orders_created"` | ||||||
|  | 	ReverseOrdersFailed  int64 `json:"reverse_orders_failed"` | ||||||
|  | 	 | ||||||
|  | 	// 响应时间统计 | ||||||
|  | 	TotalResponseTime time.Duration `json:"total_response_time"` | ||||||
|  | 	MaxResponseTime   time.Duration `json:"max_response_time"` | ||||||
|  | 	MinResponseTime   time.Duration `json:"min_response_time"` | ||||||
|  | 	 | ||||||
|  | 	// 最近更新时间 | ||||||
|  | 	LastUpdated time.Time `json:"last_updated"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PositionMetrics 持仓相关指标 | ||||||
|  | type PositionMetrics struct { | ||||||
|  | 	// 持仓同步统计 | ||||||
|  | 	SyncAttempts     int64 `json:"sync_attempts"` | ||||||
|  | 	SyncSuccesses    int64 `json:"sync_successes"` | ||||||
|  | 	SyncFailures     int64 `json:"sync_failures"` | ||||||
|  | 	 | ||||||
|  | 	// 持仓差异统计 | ||||||
|  | 	PositionMismatches int64           `json:"position_mismatches"` | ||||||
|  | 	MaxDifference      decimal.Decimal `json:"max_difference"` | ||||||
|  | 	TotalDifference    decimal.Decimal `json:"total_difference"` | ||||||
|  | 	 | ||||||
|  | 	// 平仓统计 | ||||||
|  | 	ClosePositions        int64 `json:"close_positions"` | ||||||
|  | 	ForceClosePositions   int64 `json:"force_close_positions"` | ||||||
|  | 	 | ||||||
|  | 	LastUpdated time.Time `json:"last_updated"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TakeProfitMetrics 止盈止损相关指标 | ||||||
|  | type TakeProfitMetrics struct { | ||||||
|  | 	// 止盈止损创建统计 | ||||||
|  | 	TakeProfitCreated int64 `json:"take_profit_created"` | ||||||
|  | 	StopLossCreated   int64 `json:"stop_loss_created"` | ||||||
|  | 	 | ||||||
|  | 	// 止盈止损执行统计 | ||||||
|  | 	TakeProfitExecuted int64 `json:"take_profit_executed"` | ||||||
|  | 	StopLossExecuted   int64 `json:"stop_loss_executed"` | ||||||
|  | 	 | ||||||
|  | 	// 取消统计 | ||||||
|  | 	OrdersCanceled       int64 `json:"orders_canceled"` | ||||||
|  | 	BatchCancelAttempts  int64 `json:"batch_cancel_attempts"` | ||||||
|  | 	BatchCancelSuccesses int64 `json:"batch_cancel_successes"` | ||||||
|  | 	 | ||||||
|  | 	LastUpdated time.Time `json:"last_updated"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PerformanceMetrics 性能相关指标 | ||||||
|  | type PerformanceMetrics struct { | ||||||
|  | 	// 并发统计 | ||||||
|  | 	ConcurrentRequests int64 `json:"concurrent_requests"` | ||||||
|  | 	MaxConcurrency     int64 `json:"max_concurrency"` | ||||||
|  | 	 | ||||||
|  | 	// 锁统计 | ||||||
|  | 	LockAcquisitions int64         `json:"lock_acquisitions"` | ||||||
|  | 	LockWaitTime     time.Duration `json:"lock_wait_time"` | ||||||
|  | 	LockTimeouts     int64         `json:"lock_timeouts"` | ||||||
|  | 	 | ||||||
|  | 	// 数据库操作统计 | ||||||
|  | 	DbQueries        int64         `json:"db_queries"` | ||||||
|  | 	DbTransactions   int64         `json:"db_transactions"` | ||||||
|  | 	DbQueryTime      time.Duration `json:"db_query_time"` | ||||||
|  | 	DbErrors         int64         `json:"db_errors"` | ||||||
|  | 	 | ||||||
|  | 	// API调用统计 | ||||||
|  | 	ApiCalls        int64         `json:"api_calls"` | ||||||
|  | 	ApiCallTime     time.Duration `json:"api_call_time"` | ||||||
|  | 	ApiErrors       int64         `json:"api_errors"` | ||||||
|  | 	ApiRetries      int64         `json:"api_retries"` | ||||||
|  | 	 | ||||||
|  | 	LastUpdated time.Time `json:"last_updated"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ErrorMetrics 错误相关指标 | ||||||
|  | type ErrorMetrics struct { | ||||||
|  | 	// 错误分类统计 | ||||||
|  | 	NetworkErrors    int64 `json:"network_errors"` | ||||||
|  | 	DatabaseErrors   int64 `json:"database_errors"` | ||||||
|  | 	BusinessErrors   int64 `json:"business_errors"` | ||||||
|  | 	SystemErrors     int64 `json:"system_errors"` | ||||||
|  | 	 | ||||||
|  | 	// 重试统计 | ||||||
|  | 	RetryAttempts   int64 `json:"retry_attempts"` | ||||||
|  | 	RetrySuccesses  int64 `json:"retry_successes"` | ||||||
|  | 	RetryFailures   int64 `json:"retry_failures"` | ||||||
|  | 	 | ||||||
|  | 	// 最近错误 | ||||||
|  | 	RecentErrors []ErrorRecord `json:"recent_errors"` | ||||||
|  | 	 | ||||||
|  | 	LastUpdated time.Time `json:"last_updated"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SystemMetrics 系统相关指标 | ||||||
|  | type SystemMetrics struct { | ||||||
|  | 	// 内存使用 | ||||||
|  | 	MemoryUsage    uint64  `json:"memory_usage"` | ||||||
|  | 	MemoryPercent  float64 `json:"memory_percent"` | ||||||
|  | 	 | ||||||
|  | 	// CPU使用 | ||||||
|  | 	CpuPercent float64 `json:"cpu_percent"` | ||||||
|  | 	 | ||||||
|  | 	// Goroutine统计 | ||||||
|  | 	GoroutineCount int `json:"goroutine_count"` | ||||||
|  | 	 | ||||||
|  | 	// GC统计 | ||||||
|  | 	GcCount    uint32 `json:"gc_count"` | ||||||
|  | 	GcPauseMs  uint64 `json:"gc_pause_ms"` | ||||||
|  | 	 | ||||||
|  | 	LastUpdated time.Time `json:"last_updated"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ErrorRecord 错误记录 | ||||||
|  | type ErrorRecord struct { | ||||||
|  | 	Timestamp time.Time `json:"timestamp"` | ||||||
|  | 	Type      string    `json:"type"` | ||||||
|  | 	Message   string    `json:"message"` | ||||||
|  | 	Context   string    `json:"context"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewMetricsCollector 创建新的指标收集器 | ||||||
|  | func NewMetricsCollector(config *OptimizedConfig) *MetricsCollector { | ||||||
|  | 	ctx, cancel := context.WithCancel(context.Background()) | ||||||
|  | 	 | ||||||
|  | 	return &MetricsCollector{ | ||||||
|  | 		OrderMetrics: &OrderMetrics{ | ||||||
|  | 			MinResponseTime: time.Hour, // 初始化为一个大值 | ||||||
|  | 			LastUpdated:     time.Now(), | ||||||
|  | 		}, | ||||||
|  | 		PositionMetrics: &PositionMetrics{ | ||||||
|  | 			MaxDifference:   decimal.Zero, | ||||||
|  | 			TotalDifference: decimal.Zero, | ||||||
|  | 			LastUpdated:     time.Now(), | ||||||
|  | 		}, | ||||||
|  | 		TakeProfitMetrics: &TakeProfitMetrics{ | ||||||
|  | 			LastUpdated: time.Now(), | ||||||
|  | 		}, | ||||||
|  | 		PerformanceMetrics: &PerformanceMetrics{ | ||||||
|  | 			LastUpdated: time.Now(), | ||||||
|  | 		}, | ||||||
|  | 		ErrorMetrics: &ErrorMetrics{ | ||||||
|  | 			RecentErrors: make([]ErrorRecord, 0, 100), | ||||||
|  | 			LastUpdated:  time.Now(), | ||||||
|  | 		}, | ||||||
|  | 		SystemMetrics: &SystemMetrics{ | ||||||
|  | 			LastUpdated: time.Now(), | ||||||
|  | 		}, | ||||||
|  | 		config:    config, | ||||||
|  | 		ctx:       ctx, | ||||||
|  | 		cancel:    cancel, | ||||||
|  | 		startTime: time.Now(), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Start 启动指标收集 | ||||||
|  | func (mc *MetricsCollector) Start() { | ||||||
|  | 	if !mc.config.MonitorConfig.Enabled { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	logger.Info("启动指标收集器") | ||||||
|  | 	 | ||||||
|  | 	// 启动定期收集 | ||||||
|  | 	go mc.collectLoop() | ||||||
|  | 	 | ||||||
|  | 	// 启动告警检查 | ||||||
|  | 	go mc.alertLoop() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Stop 停止指标收集 | ||||||
|  | func (mc *MetricsCollector) Stop() { | ||||||
|  | 	logger.Info("停止指标收集器") | ||||||
|  | 	mc.cancel() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // collectLoop 收集循环 | ||||||
|  | func (mc *MetricsCollector) collectLoop() { | ||||||
|  | 	ticker := time.NewTicker(mc.config.MonitorConfig.CollectInterval) | ||||||
|  | 	defer ticker.Stop() | ||||||
|  | 	 | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case <-mc.ctx.Done(): | ||||||
|  | 			return | ||||||
|  | 		case <-ticker.C: | ||||||
|  | 			mc.collectSystemMetrics() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // alertLoop 告警检查循环 | ||||||
|  | func (mc *MetricsCollector) alertLoop() { | ||||||
|  | 	ticker := time.NewTicker(time.Minute) // 每分钟检查一次 | ||||||
|  | 	defer ticker.Stop() | ||||||
|  | 	 | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case <-mc.ctx.Done(): | ||||||
|  | 			return | ||||||
|  | 		case <-ticker.C: | ||||||
|  | 			mc.checkAlerts() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordOrderProcessed 记录订单处理 | ||||||
|  | func (mc *MetricsCollector) RecordOrderProcessed(success bool, responseTime time.Duration) { | ||||||
|  | 	mc.mu.Lock() | ||||||
|  | 	defer mc.mu.Unlock() | ||||||
|  | 	 | ||||||
|  | 	atomic.AddInt64(&mc.OrderMetrics.TotalProcessed, 1) | ||||||
|  | 	 | ||||||
|  | 	if success { | ||||||
|  | 		atomic.AddInt64(&mc.OrderMetrics.SuccessfulOrders, 1) | ||||||
|  | 	} else { | ||||||
|  | 		atomic.AddInt64(&mc.OrderMetrics.FailedOrders, 1) | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// 更新响应时间统计 | ||||||
|  | 	mc.OrderMetrics.TotalResponseTime += responseTime | ||||||
|  | 	if responseTime > mc.OrderMetrics.MaxResponseTime { | ||||||
|  | 		mc.OrderMetrics.MaxResponseTime = responseTime | ||||||
|  | 	} | ||||||
|  | 	if responseTime < mc.OrderMetrics.MinResponseTime { | ||||||
|  | 		mc.OrderMetrics.MinResponseTime = responseTime | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	mc.OrderMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordOrderCanceled 记录订单取消 | ||||||
|  | func (mc *MetricsCollector) RecordOrderCanceled() { | ||||||
|  | 	atomic.AddInt64(&mc.OrderMetrics.CanceledOrders, 1) | ||||||
|  | 	mc.OrderMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordReverseOrder 记录反向订单 | ||||||
|  | func (mc *MetricsCollector) RecordReverseOrder(success bool) { | ||||||
|  | 	if success { | ||||||
|  | 		atomic.AddInt64(&mc.OrderMetrics.ReverseOrdersCreated, 1) | ||||||
|  | 	} else { | ||||||
|  | 		atomic.AddInt64(&mc.OrderMetrics.ReverseOrdersFailed, 1) | ||||||
|  | 	} | ||||||
|  | 	mc.OrderMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordPositionSync 记录持仓同步 | ||||||
|  | func (mc *MetricsCollector) RecordPositionSync(success bool, difference decimal.Decimal) { | ||||||
|  | 	mc.mu.Lock() | ||||||
|  | 	defer mc.mu.Unlock() | ||||||
|  | 	 | ||||||
|  | 	atomic.AddInt64(&mc.PositionMetrics.SyncAttempts, 1) | ||||||
|  | 	 | ||||||
|  | 	if success { | ||||||
|  | 		atomic.AddInt64(&mc.PositionMetrics.SyncSuccesses, 1) | ||||||
|  | 	} else { | ||||||
|  | 		atomic.AddInt64(&mc.PositionMetrics.SyncFailures, 1) | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// 更新差异统计 | ||||||
|  | 	if !difference.IsZero() { | ||||||
|  | 		atomic.AddInt64(&mc.PositionMetrics.PositionMismatches, 1) | ||||||
|  | 		mc.PositionMetrics.TotalDifference = mc.PositionMetrics.TotalDifference.Add(difference.Abs()) | ||||||
|  | 		 | ||||||
|  | 		if difference.Abs().GreaterThan(mc.PositionMetrics.MaxDifference) { | ||||||
|  | 			mc.PositionMetrics.MaxDifference = difference.Abs() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	mc.PositionMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordClosePosition 记录平仓 | ||||||
|  | func (mc *MetricsCollector) RecordClosePosition(forced bool) { | ||||||
|  | 	atomic.AddInt64(&mc.PositionMetrics.ClosePositions, 1) | ||||||
|  | 	if forced { | ||||||
|  | 		atomic.AddInt64(&mc.PositionMetrics.ForceClosePositions, 1) | ||||||
|  | 	} | ||||||
|  | 	mc.PositionMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordTakeProfitCreated 记录止盈止损创建 | ||||||
|  | func (mc *MetricsCollector) RecordTakeProfitCreated(orderType string) { | ||||||
|  | 	switch orderType { | ||||||
|  | 	case "TAKE_PROFIT", "TAKE_PROFIT_MARKET": | ||||||
|  | 		atomic.AddInt64(&mc.TakeProfitMetrics.TakeProfitCreated, 1) | ||||||
|  | 	case "STOP", "STOP_MARKET": | ||||||
|  | 		atomic.AddInt64(&mc.TakeProfitMetrics.StopLossCreated, 1) | ||||||
|  | 	} | ||||||
|  | 	mc.TakeProfitMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordTakeProfitExecuted 记录止盈止损执行 | ||||||
|  | func (mc *MetricsCollector) RecordTakeProfitExecuted(orderType string) { | ||||||
|  | 	switch orderType { | ||||||
|  | 	case "TAKE_PROFIT", "TAKE_PROFIT_MARKET": | ||||||
|  | 		atomic.AddInt64(&mc.TakeProfitMetrics.TakeProfitExecuted, 1) | ||||||
|  | 	case "STOP", "STOP_MARKET": | ||||||
|  | 		atomic.AddInt64(&mc.TakeProfitMetrics.StopLossExecuted, 1) | ||||||
|  | 	} | ||||||
|  | 	mc.TakeProfitMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordOrdersCanceled 记录订单取消 | ||||||
|  | func (mc *MetricsCollector) RecordOrdersCanceled(count int) { | ||||||
|  | 	atomic.AddInt64(&mc.TakeProfitMetrics.OrdersCanceled, int64(count)) | ||||||
|  | 	mc.TakeProfitMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordBatchCancel 记录批量取消 | ||||||
|  | func (mc *MetricsCollector) RecordBatchCancel(success bool) { | ||||||
|  | 	atomic.AddInt64(&mc.TakeProfitMetrics.BatchCancelAttempts, 1) | ||||||
|  | 	if success { | ||||||
|  | 		atomic.AddInt64(&mc.TakeProfitMetrics.BatchCancelSuccesses, 1) | ||||||
|  | 	} | ||||||
|  | 	mc.TakeProfitMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordLockOperation 记录锁操作 | ||||||
|  | func (mc *MetricsCollector) RecordLockOperation(acquired bool, waitTime time.Duration) { | ||||||
|  | 	atomic.AddInt64(&mc.PerformanceMetrics.LockAcquisitions, 1) | ||||||
|  | 	mc.PerformanceMetrics.LockWaitTime += waitTime | ||||||
|  | 	 | ||||||
|  | 	if !acquired { | ||||||
|  | 		atomic.AddInt64(&mc.PerformanceMetrics.LockTimeouts, 1) | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	mc.PerformanceMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordDbOperation 记录数据库操作 | ||||||
|  | func (mc *MetricsCollector) RecordDbOperation(isTransaction bool, duration time.Duration, err error) { | ||||||
|  | 	if isTransaction { | ||||||
|  | 		atomic.AddInt64(&mc.PerformanceMetrics.DbTransactions, 1) | ||||||
|  | 	} else { | ||||||
|  | 		atomic.AddInt64(&mc.PerformanceMetrics.DbQueries, 1) | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	mc.PerformanceMetrics.DbQueryTime += duration | ||||||
|  | 	 | ||||||
|  | 	if err != nil { | ||||||
|  | 		atomic.AddInt64(&mc.PerformanceMetrics.DbErrors, 1) | ||||||
|  | 		mc.RecordError("database", err.Error(), "db_operation") | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	mc.PerformanceMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordApiCall 记录API调用 | ||||||
|  | func (mc *MetricsCollector) RecordApiCall(duration time.Duration, err error, retryCount int) { | ||||||
|  | 	atomic.AddInt64(&mc.PerformanceMetrics.ApiCalls, 1) | ||||||
|  | 	mc.PerformanceMetrics.ApiCallTime += duration | ||||||
|  | 	 | ||||||
|  | 	if err != nil { | ||||||
|  | 		atomic.AddInt64(&mc.PerformanceMetrics.ApiErrors, 1) | ||||||
|  | 		mc.RecordError("api", err.Error(), "api_call") | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if retryCount > 0 { | ||||||
|  | 		atomic.AddInt64(&mc.PerformanceMetrics.ApiRetries, int64(retryCount)) | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	mc.PerformanceMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordConcurrency 记录并发数 | ||||||
|  | func (mc *MetricsCollector) RecordConcurrency(current int64) { | ||||||
|  | 	atomic.StoreInt64(&mc.PerformanceMetrics.ConcurrentRequests, current) | ||||||
|  | 	 | ||||||
|  | 	if current > atomic.LoadInt64(&mc.PerformanceMetrics.MaxConcurrency) { | ||||||
|  | 		atomic.StoreInt64(&mc.PerformanceMetrics.MaxConcurrency, current) | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	mc.PerformanceMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordError 记录错误 | ||||||
|  | func (mc *MetricsCollector) RecordError(errorType, message, context string) { | ||||||
|  | 	mc.mu.Lock() | ||||||
|  | 	defer mc.mu.Unlock() | ||||||
|  | 	 | ||||||
|  | 	// 分类统计 | ||||||
|  | 	switch errorType { | ||||||
|  | 	case "network": | ||||||
|  | 		atomic.AddInt64(&mc.ErrorMetrics.NetworkErrors, 1) | ||||||
|  | 	case "database": | ||||||
|  | 		atomic.AddInt64(&mc.ErrorMetrics.DatabaseErrors, 1) | ||||||
|  | 	case "business": | ||||||
|  | 		atomic.AddInt64(&mc.ErrorMetrics.BusinessErrors, 1) | ||||||
|  | 	default: | ||||||
|  | 		atomic.AddInt64(&mc.ErrorMetrics.SystemErrors, 1) | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// 记录最近错误 | ||||||
|  | 	errorRecord := ErrorRecord{ | ||||||
|  | 		Timestamp: time.Now(), | ||||||
|  | 		Type:      errorType, | ||||||
|  | 		Message:   message, | ||||||
|  | 		Context:   context, | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// 保持最近100个错误 | ||||||
|  | 	if len(mc.ErrorMetrics.RecentErrors) >= 100 { | ||||||
|  | 		mc.ErrorMetrics.RecentErrors = mc.ErrorMetrics.RecentErrors[1:] | ||||||
|  | 	} | ||||||
|  | 	mc.ErrorMetrics.RecentErrors = append(mc.ErrorMetrics.RecentErrors, errorRecord) | ||||||
|  | 	 | ||||||
|  | 	mc.ErrorMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RecordRetry 记录重试 | ||||||
|  | func (mc *MetricsCollector) RecordRetry(success bool) { | ||||||
|  | 	atomic.AddInt64(&mc.ErrorMetrics.RetryAttempts, 1) | ||||||
|  | 	if success { | ||||||
|  | 		atomic.AddInt64(&mc.ErrorMetrics.RetrySuccesses, 1) | ||||||
|  | 	} else { | ||||||
|  | 		atomic.AddInt64(&mc.ErrorMetrics.RetryFailures, 1) | ||||||
|  | 	} | ||||||
|  | 	mc.ErrorMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // collectSystemMetrics 收集系统指标 | ||||||
|  | func (mc *MetricsCollector) collectSystemMetrics() { | ||||||
|  | 	mc.mu.Lock() | ||||||
|  | 	defer mc.mu.Unlock() | ||||||
|  | 	 | ||||||
|  | 	// 获取内存统计 | ||||||
|  | 	var m runtime.MemStats | ||||||
|  | 	runtime.ReadMemStats(&m) | ||||||
|  | 	 | ||||||
|  | 	mc.SystemMetrics.MemoryUsage = m.Alloc | ||||||
|  | 	mc.SystemMetrics.GoroutineCount = runtime.NumGoroutine() | ||||||
|  | 	mc.SystemMetrics.GcCount = m.NumGC | ||||||
|  | 	mc.SystemMetrics.GcPauseMs = uint64(m.PauseNs[(m.NumGC+255)%256]) / 1000000 | ||||||
|  | 	 | ||||||
|  | 	mc.SystemMetrics.LastUpdated = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // checkAlerts 检查告警 | ||||||
|  | func (mc *MetricsCollector) checkAlerts() { | ||||||
|  | 	thresholds := mc.config.MonitorConfig.AlertThresholds | ||||||
|  | 	 | ||||||
|  | 	// 检查订单失败率 | ||||||
|  | 	if mc.OrderMetrics.TotalProcessed > 0 { | ||||||
|  | 		failureRate := float64(mc.OrderMetrics.FailedOrders) / float64(mc.OrderMetrics.TotalProcessed) * 100 | ||||||
|  | 		if failureRate > thresholds.OrderFailureRate { | ||||||
|  | 			logger.Warnf("订单失败率告警: %.2f%% (阈值: %.2f%%)", failureRate, thresholds.OrderFailureRate) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// 检查持仓同步失败率 | ||||||
|  | 	if mc.PositionMetrics.SyncAttempts > 0 { | ||||||
|  | 		syncFailureRate := float64(mc.PositionMetrics.SyncFailures) / float64(mc.PositionMetrics.SyncAttempts) * 100 | ||||||
|  | 		if syncFailureRate > thresholds.PositionSyncFailureRate { | ||||||
|  | 			logger.Warnf("持仓同步失败率告警: %.2f%% (阈值: %.2f%%)", syncFailureRate, thresholds.PositionSyncFailureRate) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// 检查API失败率 | ||||||
|  | 	if mc.PerformanceMetrics.ApiCalls > 0 { | ||||||
|  | 		apiFailureRate := float64(mc.PerformanceMetrics.ApiErrors) / float64(mc.PerformanceMetrics.ApiCalls) * 100 | ||||||
|  | 		if apiFailureRate > thresholds.ApiFailureRate { | ||||||
|  | 			logger.Warnf("API失败率告警: %.2f%% (阈值: %.2f%%)", apiFailureRate, thresholds.ApiFailureRate) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// 检查内存使用率 | ||||||
|  | 	if mc.SystemMetrics.MemoryPercent > thresholds.MemoryUsageThreshold { | ||||||
|  | 		logger.Warnf("内存使用率告警: %.2f%% (阈值: %.2f%%)", mc.SystemMetrics.MemoryPercent, thresholds.MemoryUsageThreshold) | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// 检查CPU使用率 | ||||||
|  | 	if mc.SystemMetrics.CpuPercent > thresholds.CpuUsageThreshold { | ||||||
|  | 		logger.Warnf("CPU使用率告警: %.2f%% (阈值: %.2f%%)", mc.SystemMetrics.CpuPercent, thresholds.CpuUsageThreshold) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetMetrics 获取所有指标 | ||||||
|  | func (mc *MetricsCollector) GetMetrics() map[string]interface{} { | ||||||
|  | 	mc.mu.RLock() | ||||||
|  | 	defer mc.mu.RUnlock() | ||||||
|  | 	 | ||||||
|  | 	return map[string]interface{}{ | ||||||
|  | 		"order_metrics":       mc.OrderMetrics, | ||||||
|  | 		"position_metrics":    mc.PositionMetrics, | ||||||
|  | 		"take_profit_metrics": mc.TakeProfitMetrics, | ||||||
|  | 		"performance_metrics": mc.PerformanceMetrics, | ||||||
|  | 		"error_metrics":       mc.ErrorMetrics, | ||||||
|  | 		"system_metrics":      mc.SystemMetrics, | ||||||
|  | 		"uptime":              time.Since(mc.startTime).String(), | ||||||
|  | 		"collected_at":        time.Now(), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetSummary 获取指标摘要 | ||||||
|  | func (mc *MetricsCollector) GetSummary() map[string]interface{} { | ||||||
|  | 	mc.mu.RLock() | ||||||
|  | 	defer mc.mu.RUnlock() | ||||||
|  | 	 | ||||||
|  | 	summary := make(map[string]interface{}) | ||||||
|  | 	 | ||||||
|  | 	// 订单摘要 | ||||||
|  | 	if mc.OrderMetrics.TotalProcessed > 0 { | ||||||
|  | 		successRate := float64(mc.OrderMetrics.SuccessfulOrders) / float64(mc.OrderMetrics.TotalProcessed) * 100 | ||||||
|  | 		avgResponseTime := mc.OrderMetrics.TotalResponseTime / time.Duration(mc.OrderMetrics.TotalProcessed) | ||||||
|  | 		 | ||||||
|  | 		summary["order_success_rate"] = fmt.Sprintf("%.2f%%", successRate) | ||||||
|  | 		summary["avg_response_time"] = avgResponseTime.String() | ||||||
|  | 		summary["total_orders"] = mc.OrderMetrics.TotalProcessed | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// 持仓摘要 | ||||||
|  | 	if mc.PositionMetrics.SyncAttempts > 0 { | ||||||
|  | 		syncSuccessRate := float64(mc.PositionMetrics.SyncSuccesses) / float64(mc.PositionMetrics.SyncAttempts) * 100 | ||||||
|  | 		summary["position_sync_rate"] = fmt.Sprintf("%.2f%%", syncSuccessRate) | ||||||
|  | 		summary["position_mismatches"] = mc.PositionMetrics.PositionMismatches | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// 性能摘要 | ||||||
|  | 	summary["current_concurrency"] = mc.PerformanceMetrics.ConcurrentRequests | ||||||
|  | 	summary["max_concurrency"] = mc.PerformanceMetrics.MaxConcurrency | ||||||
|  | 	summary["total_api_calls"] = mc.PerformanceMetrics.ApiCalls | ||||||
|  | 	summary["total_db_queries"] = mc.PerformanceMetrics.DbQueries | ||||||
|  | 	 | ||||||
|  | 	// 系统摘要 | ||||||
|  | 	summary["memory_usage_mb"] = mc.SystemMetrics.MemoryUsage / 1024 / 1024 | ||||||
|  | 	summary["goroutine_count"] = mc.SystemMetrics.GoroutineCount | ||||||
|  | 	summary["uptime"] = time.Since(mc.startTime).String() | ||||||
|  | 	 | ||||||
|  | 	return summary | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 全局指标收集器实例 | ||||||
|  | var GlobalMetricsCollector *MetricsCollector | ||||||
|  |  | ||||||
|  | // InitMetricsCollector 初始化指标收集器 | ||||||
|  | func InitMetricsCollector(config *OptimizedConfig) { | ||||||
|  | 	GlobalMetricsCollector = NewMetricsCollector(config) | ||||||
|  | 	GlobalMetricsCollector.Start() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetMetricsCollector 获取全局指标收集器 | ||||||
|  | func GetMetricsCollector() *MetricsCollector { | ||||||
|  | 	return GlobalMetricsCollector | ||||||
|  | } | ||||||
| @ -170,13 +170,15 @@ func (e *ReverseService) ReverseOrder(apiKey string, mapData map[string]interfac | |||||||
| 			switch { | 			switch { | ||||||
| 			case mainOrder.PositionSide == "LONG" && mainOrder.Side == "BUY", mainOrder.PositionSide == "SHORT" && mainOrder.Side == "SELL": | 			case mainOrder.PositionSide == "LONG" && mainOrder.Side == "BUY", mainOrder.PositionSide == "SHORT" && mainOrder.Side == "SELL": | ||||||
| 				if mainOrder.Category == 0 { | 				if mainOrder.Category == 0 { | ||||||
| 					if _, _, err1 := e.savePosition(&mainOrder, &apiInfo, false, false); err1 != nil { | 					if _, _, err1 := e.savePositionWithRetry(&mainOrder, &apiInfo, false, false); err1 != nil { | ||||||
|  | 						e.Log.Errorf("反向订单持仓保存失败: %v", err1) | ||||||
| 						return true, err1 | 						return true, err1 | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SrgetELL": | 			case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SELL": | ||||||
| 				if mainOrder.Category == 0 { | 				if mainOrder.Category == 0 { | ||||||
| 					if _, _, err1 := e.savePosition(&mainOrder, &apiInfo, false, true); err1 != nil { | 					if _, _, err1 := e.savePositionWithRetry(&mainOrder, &apiInfo, false, true); err1 != nil { | ||||||
|  | 						e.Log.Errorf("反向订单持仓保存失败: %v", err1) | ||||||
| 						return true, err1 | 						return true, err1 | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| @ -307,6 +309,20 @@ func (e *ReverseService) SaveMainOrder(mapData map[string]interface{}, apiInfo D | |||||||
| 	return reverseOrder, nil | 	return reverseOrder, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // savePositionWithRetry 带重试机制的持仓保存 | ||||||
|  | func (e *ReverseService) savePositionWithRetry(order *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, isMain, reducePosition bool) (bool, bool, error) { | ||||||
|  | 	const maxRetries = 3 | ||||||
|  | 	for i := 0; i < maxRetries; i++ { | ||||||
|  | 		needReverseOrder, closePosition, err := e.savePosition(order, apiInfo, isMain, reducePosition) | ||||||
|  | 		if err == nil { | ||||||
|  | 			return needReverseOrder, closePosition, nil | ||||||
|  | 		} | ||||||
|  | 		e.Log.Warnf("持仓保存失败,重试 %d/%d: %v", i+1, maxRetries, err) | ||||||
|  | 		time.Sleep(time.Millisecond * 100 * time.Duration(i+1)) | ||||||
|  | 	} | ||||||
|  | 	return false, false, fmt.Errorf("持仓保存失败,已重试 %d 次", maxRetries) | ||||||
|  | } | ||||||
|  |  | ||||||
| // 更新仓位信息 | // 更新仓位信息 | ||||||
| // apiInfo: 当前下单api信息 | // apiInfo: 当前下单api信息 | ||||||
| // return | // return | ||||||
| @ -364,25 +380,27 @@ func (e *ReverseService) savePosition(order *DbModels.LineReverseOrder, apiInfo | |||||||
| 		//平仓 | 		//平仓 | ||||||
| 		if closePosition { | 		if closePosition { | ||||||
| 			totalNum = decimal.Zero | 			totalNum = decimal.Zero | ||||||
| 			sqlStr = "UPDATE line_reverse_position set amount=@totalNum,updated_at=now(),status=2 where id =@id and status!=2 " | 			sqlStr = "UPDATE line_reverse_position set amount=@totalNum,updated_at=now(),status=2,version=version+1 where id =@id and status!=2 and version=@version" | ||||||
| 		} else if reducePosition { | 		} else if reducePosition { | ||||||
| 			//只减仓 | 			//只减仓 | ||||||
| 			sqlStr = "UPDATE line_reverse_position set amount=amount - @totalNum,updated_at=now(),average_price=@averagePrice where id =@id and status!=2 " | 			sqlStr = "UPDATE line_reverse_position set amount=amount - @totalNum,updated_at=now(),average_price=@averagePrice,version=version+1 where id =@id and status!=2 and version=@version" | ||||||
| 		} else { | 		} else { | ||||||
| 			sqlStr = "UPDATE line_reverse_position set total_amount=total_amount + @totalNum,amount=amount + @totalNum,updated_at=now(),average_price=@averagePrice where id =@id and status!=2" | 			sqlStr = "UPDATE line_reverse_position set total_amount=total_amount + @totalNum,amount=amount + @totalNum,updated_at=now(),average_price=@averagePrice,version=version+1 where id =@id and status!=2 and version=@version" | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		querySql = "reverse_api_id =? and position_side =? and symbol =? and reverse_status in (0,1)" | 		querySql = "reverse_api_id =? and position_side =? and symbol =? and reverse_status in (0,1)" | ||||||
| 		//减仓 判断是否为平仓 | 		//减仓 判断是否为平仓 | ||||||
| 		closePosition = e.getClosePosition(reducePosition, apiInfo, order, order.PositionSide, isMain) | 		closePosition = e.getClosePosition(reducePosition, apiInfo, order, order.PositionSide, isMain) | ||||||
|  |  | ||||||
|  | 		// 关键修复:确保反向持仓状态正确更新 | ||||||
| 		if closePosition { | 		if closePosition { | ||||||
| 			totalNum = decimal.Zero | 			totalNum = decimal.Zero | ||||||
| 			sqlStr = "UPDATE line_reverse_position set reverse_amount=@totalNum,updated_at=now(),reverse_status=2 where id =@id and reverse_status !=2" | 			// 增强的平仓更新逻辑,确保状态正确设置为2 | ||||||
|  | 			sqlStr = "UPDATE line_reverse_position set reverse_amount=@totalNum,updated_at=now(),reverse_status=2,version=version+1 where id =@id and reverse_status !=2 and version=@version" | ||||||
| 		} else if reducePosition { | 		} else if reducePosition { | ||||||
| 			sqlStr = "UPDATE line_reverse_position set reverse_amount=reverse_amount - @totalNum,updated_at=now(),reverse_average_price=@averagePrice where id =@id and reverse_status !=2" | 			sqlStr = "UPDATE line_reverse_position set reverse_amount=reverse_amount - @totalNum,updated_at=now(),reverse_average_price=@averagePrice,version=version+1 where id =@id and reverse_status !=2 and version=@version" | ||||||
| 		} else { | 		} else { | ||||||
| 			sqlStr = "UPDATE line_reverse_position set total_reverse_amount=total_reverse_amount + @totalNum,reverse_amount=reverse_amount + @totalNum,updated_at=now(),reverse_status =1,reverse_average_price=@averagePrice where id =@id and reverse_status !=2" | 			sqlStr = "UPDATE line_reverse_position set total_reverse_amount=total_reverse_amount + @totalNum,reverse_amount=reverse_amount + @totalNum,updated_at=now(),reverse_status =1,reverse_average_price=@averagePrice,version=version+1 where id =@id and reverse_status !=2 and version=@version" | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -390,22 +408,23 @@ func (e *ReverseService) savePosition(order *DbModels.LineReverseOrder, apiInfo | |||||||
| 	var remainQuantity decimal.Decimal | 	var remainQuantity decimal.Decimal | ||||||
|  |  | ||||||
| 	err := e.Orm.Transaction(func(tx *gorm.DB) error { | 	err := e.Orm.Transaction(func(tx *gorm.DB) error { | ||||||
| 		err1 := tx.Model(&position).Where(querySql, | 			err1 := tx.Model(&position).Where(querySql, | ||||||
| 			order.ApiId, positionSide, order.Symbol).First(&position).Error | 				order.ApiId, positionSide, order.Symbol).First(&position).Error | ||||||
|  |  | ||||||
| 		if err1 != nil { | 			if err1 != nil { | ||||||
| 			//主单仓位不存在,创建新仓位 | 				//主单仓位不存在,创建新仓位 | ||||||
| 			if isMain && errors.Is(err1, gorm.ErrRecordNotFound) && !reducePosition { | 				if isMain && errors.Is(err1, gorm.ErrRecordNotFound) && !reducePosition { | ||||||
|  | 					// 初始化版本号 | ||||||
|  | 					position.Version = 0 | ||||||
|  | 					if err2 := tx.Create(&position).Error; err2 != nil { | ||||||
|  | 						return err2 | ||||||
|  | 					} | ||||||
|  |  | ||||||
| 				if err2 := tx.Create(&position).Error; err2 != nil { | 					averagePrice = position.AveragePrice | ||||||
| 					return err2 | 				} else { | ||||||
|  | 					return err1 | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				averagePrice = position.AveragePrice |  | ||||||
| 			} else { | 			} else { | ||||||
| 				return err1 |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			var totalAmount decimal.Decimal | 			var totalAmount decimal.Decimal | ||||||
| 			var totalPrice decimal.Decimal | 			var totalPrice decimal.Decimal | ||||||
| 			if !position.Amount.IsZero() && !position.AveragePrice.IsZero() { | 			if !position.Amount.IsZero() && !position.AveragePrice.IsZero() { | ||||||
| @ -457,16 +476,17 @@ func (e *ReverseService) savePosition(order *DbModels.LineReverseOrder, apiInfo | |||||||
| 			return err2 | 			return err2 | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		dbResult := tx.Exec(sqlStr, sql.Named("totalNum", totalNum), sql.Named("id", position.Id), sql.Named("averagePrice", averagePrice)) | 		// 使用乐观锁执行更新 | ||||||
| 		if dbResult.Error != nil { | 			dbResult := tx.Exec(sqlStr, sql.Named("totalNum", totalNum), sql.Named("id", position.Id), sql.Named("averagePrice", averagePrice), sql.Named("version", position.Version)) | ||||||
| 			return dbResult.Error | 			if dbResult.Error != nil { | ||||||
| 		} | 				return dbResult.Error | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 		order.PositionId = position.Id | 			order.PositionId = position.Id | ||||||
| 		if dbResult.RowsAffected == 0 { | 			if dbResult.RowsAffected == 0 { | ||||||
| 			e.Log.Errorf("减仓数据 是否平仓单:%v :%v", closePosition, order) | 				e.Log.Errorf("减仓数据 是否平仓单:%v :%v, 可能存在并发冲突", closePosition, order) | ||||||
| 			return errors.New("没有找到对应的持仓信息") | 				return fmt.Errorf("没有找到对应的持仓信息或版本冲突,position_id:%d, version:%d", position.Id, position.Version) | ||||||
| 		} | 			} | ||||||
|  |  | ||||||
| 		if reducePosition && !isMain { | 		if reducePosition && !isMain { | ||||||
| 			remainQuantity = position.ReverseAmount.Sub(totalNum) | 			remainQuantity = position.ReverseAmount.Sub(totalNum) | ||||||
| @ -1052,145 +1072,3 @@ func getOrderType(t string) int { | |||||||
| 	} | 	} | ||||||
| 	return 2 | 	return 2 | ||||||
| } | } | ||||||
|  |  | ||||||
| // // 重下止盈止损 |  | ||||||
| // // mapData: 主单止盈止损回调 |  | ||||||
| // func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orderSn string, mainApiInfo *DbModels.LineApiUser, symbol *models.TradeSet) error { |  | ||||||
| // 	orderType := 0 //订单类型 1-止盈 2-止损 |  | ||||||
| // 	side, err := maphelper.GetString(*mapData, "S") |  | ||||||
|  |  | ||||||
| // 	if err != nil { |  | ||||||
| // 		return err |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	ot, err := maphelper.GetString(*mapData, "ot") |  | ||||||
|  |  | ||||||
| // 	if err != nil { |  | ||||||
| // 		return err |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	//反单止盈止损方向相反 |  | ||||||
| // 	if side == "SELL" { |  | ||||||
| // 		side = "BUY" |  | ||||||
| // 	} else { |  | ||||||
| // 		side = "SELL" |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	positionSide, err := maphelper.GetString(*mapData, "ps") |  | ||||||
|  |  | ||||||
| // 	if err != nil { |  | ||||||
| // 		return err |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	if positionSide == "LONG" { |  | ||||||
| // 		positionSide = "SHORT" |  | ||||||
| // 	} else { |  | ||||||
| // 		positionSide = "LONG" |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	close := maphelper.GetBool(*mapData, "cp") |  | ||||||
| // 	stopPrice := maphelper.GetDecimal(*mapData, "sp") |  | ||||||
| // 	if stopPrice.IsZero() { |  | ||||||
| // 		e.Log.Errorf("获取止盈止损单触发价失败 symbol:%s custom:%s :%v", symbol, orderSn, err) |  | ||||||
| // 		return err |  | ||||||
| // 	} |  | ||||||
| // 	apiInfo, err := GetApiInfo(mainApiInfo.ReverseApiId) |  | ||||||
|  |  | ||||||
| // 	if err != nil { |  | ||||||
| // 		e.Log.Errorf("根据主单api获取反单api失败 symbol:%s custom:%s :%v", symbol, orderSn, err) |  | ||||||
| // 		return err |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	switch ot { |  | ||||||
| // 	case "STOP_MARKET", "STOP": |  | ||||||
| // 		orderType = 2 |  | ||||||
| // 	case "TAKE_PROFIT_MARKET", "TAKE_PROFIT": |  | ||||||
| // 		orderType = 1 |  | ||||||
| // 	default: |  | ||||||
| // 		return fmt.Errorf("不支持的订单类型 ot:%s", ot) |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	var reversePosition DbModels.LineReversePosition |  | ||||||
|  |  | ||||||
| // 	e.Orm.Model(&reversePosition). |  | ||||||
| // 		Where("symbol =? and reverse_api_id =? and position_side =? and reverse_status =1", symbol.GetSymbol(), apiInfo.Id, positionSide). |  | ||||||
| // 		First(&reversePosition) |  | ||||||
|  |  | ||||||
| // 	if reversePosition.Id == 0 { |  | ||||||
| // 		e.Log.Errorf("获取反单持仓失败 symbol:%s custom:%s :%v", symbol, orderSn, err) |  | ||||||
| // 		return err |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	mainPercent := decimal.NewFromInt(1) |  | ||||||
|  |  | ||||||
| // 	if !stopPrice.IsZero() && !reversePosition.AveragePrice.IsZero() { |  | ||||||
| // 		mainPercent = stopPrice.Div(reversePosition.AveragePrice) |  | ||||||
| // 		mainPercent = (mainPercent.Sub(decimal.NewFromInt(1))).Abs().Truncate(4) |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	var percent decimal.Decimal |  | ||||||
| // 	switch { |  | ||||||
| // 	//做多止损 |  | ||||||
| // 	case orderType == 2 && positionSide == "LONG", orderType == 1 && positionSide == "SHORT": |  | ||||||
| // 		percent = decimal.NewFromInt(1).Sub(mainPercent) |  | ||||||
| // 	case orderType == 2 && positionSide == "SHORT", orderType == 1 && positionSide == "LONG": |  | ||||||
| // 		percent = decimal.NewFromInt(1).Add(mainPercent) |  | ||||||
| // 	default: |  | ||||||
| // 		return fmt.Errorf("不支持的订单类型 ot:%s, ps:%s", ot, positionSide) |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	now := time.Now() |  | ||||||
| // 	price := reversePosition.AveragePrice.Mul(percent).Truncate(int32(symbol.PriceDigit)) |  | ||||||
| // 	lastPrice, _ := decimal.NewFromString(symbol.LastPrice) |  | ||||||
| // 	newOrder := DbModels.LineReverseOrder{ |  | ||||||
| // 		PositionId:    reversePosition.Id, |  | ||||||
| // 		OrderSn:       helper.GetOrderNo(), |  | ||||||
| // 		OrderType:     orderType, |  | ||||||
| // 		Status:        1, |  | ||||||
| // 		Price:         price, |  | ||||||
| // 		TotalNum:      reversePosition.TotalReverseAmount, |  | ||||||
| // 		Symbol:        symbol.GetSymbol(), |  | ||||||
| // 		Side:          side, |  | ||||||
| // 		PositionSide:  positionSide, |  | ||||||
| // 		FollowOrderSn: orderSn, |  | ||||||
| // 		Type:          ot, |  | ||||||
| // 		SignPrice:     lastPrice, |  | ||||||
| // 		Category:      1, |  | ||||||
| // 		ApiId:         apiInfo.Id, |  | ||||||
| // 		IsAddPosition: 2, |  | ||||||
| // 		TriggerTime:   &now, |  | ||||||
| // 		BuyPrice:      reversePosition.TotalReverseAmount.Mul(price).Truncate(int32(symbol.PriceDigit)), |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	if err1 := e.Orm.Create(&newOrder).Error; err1 != nil { |  | ||||||
| // 		e.Log.Errorf("保存反单止盈止损失败 symbol:%s custom:%s :%v", symbol, orderSn, err1) |  | ||||||
| // 		return err1 |  | ||||||
| // 	} |  | ||||||
| // 	params := FutOrderPlace{ |  | ||||||
| // 		ApiId:            apiInfo.Id, |  | ||||||
| // 		Symbol:           symbol.GetSymbol(), |  | ||||||
| // 		PositionSide:     newOrder.PositionSide, |  | ||||||
| // 		Side:             newOrder.Side, |  | ||||||
| // 		OrderType:        ot, |  | ||||||
| // 		Quantity:         newOrder.TotalNum, |  | ||||||
| // 		Price:            price, |  | ||||||
| // 		StopPrice:        price, |  | ||||||
| // 		Profit:           price, |  | ||||||
| // 		NewClientOrderId: newOrder.OrderSn, |  | ||||||
| // 		ClosePosition:    close, |  | ||||||
| // 	} |  | ||||||
| // 	futApiV2 := FuturesResetV2{Service: e.Service} |  | ||||||
| // 	err = futApiV2.OrderPlaceLoop(&apiInfo, params) |  | ||||||
|  |  | ||||||
| // 	if err != nil { |  | ||||||
| // 		e.Log.Errorf("币安下单失败 symbol:%s custom:%s :%v", symbol.GetSymbol(), orderSn, err) |  | ||||||
|  |  | ||||||
| // 		if err1 := e.Orm.Model(&newOrder).Updates(map[string]interface{}{"status": 8, "remark": err.Error(), "updated_at": time.Now()}).Error; err1 != nil { |  | ||||||
| // 			e.Log.Errorf("更新订单状态失败 symbol:%s custom:%s :%v", newOrder.Symbol, newOrder.OrderSn, err1) |  | ||||||
| // 		} |  | ||||||
| // 		return err |  | ||||||
| // 	} |  | ||||||
|  |  | ||||||
| // 	e.DoCancelTakeProfitBatch(symbol.GetSymbol(), newOrder.PositionSide, newOrder.Side, newOrder.OrderType, &apiInfo) |  | ||||||
| // 	return nil |  | ||||||
| // } |  | ||||||
|  | |||||||
							
								
								
									
										1121
									
								
								services/binanceservice/reverse_service_optimized.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1121
									
								
								services/binanceservice/reverse_service_optimized.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										457
									
								
								services/binanceservice/transaction_manager_optimized.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										457
									
								
								services/binanceservice/transaction_manager_optimized.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,457 @@ | |||||||
|  | package binanceservice | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/logger" | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // TransactionManager 事务管理器 | ||||||
|  | type TransactionManager struct { | ||||||
|  | 	db      *gorm.DB | ||||||
|  | 	metrics *MetricsCollector | ||||||
|  | 	config  *OptimizedConfig | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TransactionConfig 事务配置 | ||||||
|  | type TransactionConfig struct { | ||||||
|  | 	Timeout        time.Duration `json:"timeout"` | ||||||
|  | 	RetryAttempts  int           `json:"retry_attempts"` | ||||||
|  | 	RetryDelay     time.Duration `json:"retry_delay"` | ||||||
|  | 	IsolationLevel string        `json:"isolation_level"` | ||||||
|  | 	ReadOnly       bool          `json:"read_only"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TransactionContext 事务上下文 | ||||||
|  | type TransactionContext struct { | ||||||
|  | 	Tx        *gorm.DB | ||||||
|  | 	StartTime time.Time | ||||||
|  | 	Config    TransactionConfig | ||||||
|  | 	Context   context.Context | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TransactionResult 事务结果 | ||||||
|  | type TransactionResult struct { | ||||||
|  | 	Success   bool          `json:"success"` | ||||||
|  | 	Duration  time.Duration `json:"duration"` | ||||||
|  | 	Error     error         `json:"error"` | ||||||
|  | 	Retries   int           `json:"retries"` | ||||||
|  | 	Timestamp time.Time     `json:"timestamp"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewTransactionManager 创建事务管理器 | ||||||
|  | func NewTransactionManager(db *gorm.DB, metrics *MetricsCollector, config *OptimizedConfig) *TransactionManager { | ||||||
|  | 	return &TransactionManager{ | ||||||
|  | 		db:      db, | ||||||
|  | 		metrics: metrics, | ||||||
|  | 		config:  config, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithTransaction 在事务中执行操作 | ||||||
|  | func (tm *TransactionManager) WithTransaction(ctx context.Context, config TransactionConfig, operation func(*TransactionContext) error) *TransactionResult { | ||||||
|  | 	startTime := time.Now() | ||||||
|  | 	result := &TransactionResult{ | ||||||
|  | 		Timestamp: startTime, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 设置默认配置 | ||||||
|  | 	if config.Timeout == 0 { | ||||||
|  | 		config.Timeout = 30 * time.Second // 默认事务超时时间 | ||||||
|  | 	} | ||||||
|  | 	if config.RetryAttempts == 0 { | ||||||
|  | 		config.RetryAttempts = tm.config.RetryConfig.MaxRetries | ||||||
|  | 	} | ||||||
|  | 	if config.RetryDelay == 0 { | ||||||
|  | 		config.RetryDelay = tm.config.RetryConfig.RetryDelay | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 执行事务(带重试) | ||||||
|  | 	for attempt := 1; attempt <= config.RetryAttempts; attempt++ { | ||||||
|  | 		err := tm.executeTransaction(ctx, config, operation) | ||||||
|  | 		if err == nil { | ||||||
|  | 			result.Success = true | ||||||
|  | 			result.Duration = time.Since(startTime) | ||||||
|  | 			result.Retries = attempt - 1 | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		result.Error = err | ||||||
|  | 		result.Retries = attempt | ||||||
|  |  | ||||||
|  | 		// 检查是否可重试 | ||||||
|  | 		if !tm.isRetryableError(err) || attempt == config.RetryAttempts { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 等待重试 | ||||||
|  | 		logger.Warnf("事务执行失败,第 %d 次重试: %v", attempt, err) | ||||||
|  | 		time.Sleep(config.RetryDelay * time.Duration(attempt)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	result.Duration = time.Since(startTime) | ||||||
|  |  | ||||||
|  | 	// 记录指标 | ||||||
|  | 	if tm.metrics != nil { | ||||||
|  | 		tm.metrics.RecordDbOperation(true, result.Duration, result.Error) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // executeTransaction 执行单次事务 | ||||||
|  | func (tm *TransactionManager) executeTransaction(ctx context.Context, config TransactionConfig, operation func(*TransactionContext) error) error { | ||||||
|  | 	// 设置事务超时 | ||||||
|  | 	txCtx := ctx | ||||||
|  | 	if config.Timeout > 0 { | ||||||
|  | 		var cancel context.CancelFunc | ||||||
|  | 		txCtx, cancel = context.WithTimeout(ctx, config.Timeout) | ||||||
|  | 		defer cancel() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 开始事务 | ||||||
|  | 	tx := tm.db.WithContext(txCtx).Begin() | ||||||
|  | 	if tx.Error != nil { | ||||||
|  | 		return fmt.Errorf("开始事务失败: %w", tx.Error) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 设置事务隔离级别 | ||||||
|  | 	if config.IsolationLevel != "" { | ||||||
|  | 		if err := tx.Exec(fmt.Sprintf("SET TRANSACTION ISOLATION LEVEL %s", config.IsolationLevel)).Error; err != nil { | ||||||
|  | 			tx.Rollback() | ||||||
|  | 			return fmt.Errorf("设置事务隔离级别失败: %w", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 设置只读模式 | ||||||
|  | 	if config.ReadOnly { | ||||||
|  | 		if err := tx.Exec("SET TRANSACTION READ ONLY").Error; err != nil { | ||||||
|  | 			tx.Rollback() | ||||||
|  | 			return fmt.Errorf("设置只读事务失败: %w", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 创建事务上下文 | ||||||
|  | 	txContext := &TransactionContext{ | ||||||
|  | 		Tx:        tx, | ||||||
|  | 		StartTime: time.Now(), | ||||||
|  | 		Config:    config, | ||||||
|  | 		Context:   txCtx, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 执行操作 | ||||||
|  | 	err := operation(txContext) | ||||||
|  | 	if err != nil { | ||||||
|  | 		// 回滚事务 | ||||||
|  | 		if rollbackErr := tx.Rollback().Error; rollbackErr != nil { | ||||||
|  | 			logger.Errorf("事务回滚失败: %v", rollbackErr) | ||||||
|  | 			return fmt.Errorf("操作失败且回滚失败: %w, 回滚错误: %v", err, rollbackErr) | ||||||
|  | 		} | ||||||
|  | 		return fmt.Errorf("事务操作失败: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 提交事务 | ||||||
|  | 	if commitErr := tx.Commit().Error; commitErr != nil { | ||||||
|  | 		return fmt.Errorf("事务提交失败: %w", commitErr) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithOptimisticLock 使用乐观锁执行操作 | ||||||
|  | func (tm *TransactionManager) WithOptimisticLock(ctx context.Context, config TransactionConfig, operation func(*TransactionContext) error) *TransactionResult { | ||||||
|  | 	return tm.WithTransaction(ctx, config, func(txCtx *TransactionContext) error { | ||||||
|  | 		// 在乐观锁模式下,我们需要在操作中检查版本号 | ||||||
|  | 		return operation(txCtx) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SaveWithOptimisticLock 使用乐观锁保存数据 | ||||||
|  | func (tm *TransactionManager) SaveWithOptimisticLock(ctx context.Context, model interface{}, condition string, args ...interface{}) error { | ||||||
|  | 	config := TransactionConfig{ | ||||||
|  | 		Timeout:       30 * time.Second, | ||||||
|  | 		RetryAttempts: 3, | ||||||
|  | 		RetryDelay:    100 * time.Millisecond, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	result := tm.WithTransaction(ctx, config, func(txCtx *TransactionContext) error { | ||||||
|  | 		// 使用 WHERE 条件和版本号进行更新 | ||||||
|  | 		result := txCtx.Tx.Where(condition, args...).Save(model) | ||||||
|  | 		if result.Error != nil { | ||||||
|  | 			return result.Error | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 检查是否有行被更新 | ||||||
|  | 		if result.RowsAffected == 0 { | ||||||
|  | 			return fmt.Errorf("乐观锁冲突: 数据已被其他进程修改") | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	return result.Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdateWithOptimisticLock 使用乐观锁更新数据 | ||||||
|  | func (tm *TransactionManager) UpdateWithOptimisticLock(ctx context.Context, model interface{}, updates map[string]interface{}, condition string, args ...interface{}) error { | ||||||
|  | 	config := TransactionConfig{ | ||||||
|  | 		Timeout:       30 * time.Second, | ||||||
|  | 		RetryAttempts: 3, | ||||||
|  | 		RetryDelay:    100 * time.Millisecond, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	result := tm.WithTransaction(ctx, config, func(txCtx *TransactionContext) error { | ||||||
|  | 		// 增加版本号 | ||||||
|  | 		if _, hasVersion := updates["version"]; !hasVersion { | ||||||
|  | 			updates["version"] = gorm.Expr("version + 1") | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 执行更新 | ||||||
|  | 		result := txCtx.Tx.Model(model).Where(condition, args...).Updates(updates) | ||||||
|  | 		if result.Error != nil { | ||||||
|  | 			return result.Error | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 检查是否有行被更新 | ||||||
|  | 		if result.RowsAffected == 0 { | ||||||
|  | 			return fmt.Errorf("乐观锁冲突: 数据已被其他进程修改") | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	return result.Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BatchOperation 批量操作 | ||||||
|  | func (tm *TransactionManager) BatchOperation(ctx context.Context, config TransactionConfig, operations []func(*TransactionContext) error) *TransactionResult { | ||||||
|  | 	return tm.WithTransaction(ctx, config, func(txCtx *TransactionContext) error { | ||||||
|  | 		for i, operation := range operations { | ||||||
|  | 			if err := operation(txCtx); err != nil { | ||||||
|  | 				return fmt.Errorf("批量操作第 %d 步失败: %w", i+1, err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateSavepoint 创建保存点 | ||||||
|  | func (tm *TransactionManager) CreateSavepoint(txCtx *TransactionContext, name string) error { | ||||||
|  | 	return txCtx.Tx.Exec(fmt.Sprintf("SAVEPOINT %s", name)).Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RollbackToSavepoint 回滚到保存点 | ||||||
|  | func (tm *TransactionManager) RollbackToSavepoint(txCtx *TransactionContext, name string) error { | ||||||
|  | 	return txCtx.Tx.Exec(fmt.Sprintf("ROLLBACK TO SAVEPOINT %s", name)).Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ReleaseSavepoint 释放保存点 | ||||||
|  | func (tm *TransactionManager) ReleaseSavepoint(txCtx *TransactionContext, name string) error { | ||||||
|  | 	return txCtx.Tx.Exec(fmt.Sprintf("RELEASE SAVEPOINT %s", name)).Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isRetryableError 判断错误是否可重试 | ||||||
|  | func (tm *TransactionManager) isRetryableError(err error) bool { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	errorMsg := err.Error() | ||||||
|  | 	 | ||||||
|  | 	// 可重试的错误类型 | ||||||
|  | 	retryableErrors := []string{ | ||||||
|  | 		"deadlock", | ||||||
|  | 		"lock wait timeout", | ||||||
|  | 		"connection", | ||||||
|  | 		"timeout", | ||||||
|  | 		"temporary", | ||||||
|  | 		"乐观锁冲突", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, retryableError := range retryableErrors { | ||||||
|  | 		if strings.Contains(errorMsg, retryableError) { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetTransactionStats 获取事务统计信息 | ||||||
|  | func (tm *TransactionManager) GetTransactionStats() map[string]interface{} { | ||||||
|  | 	stats := make(map[string]interface{}) | ||||||
|  |  | ||||||
|  | 	// 从指标收集器获取数据库相关统计 | ||||||
|  | 	if tm.metrics != nil { | ||||||
|  | 		stats["total_transactions"] = tm.metrics.PerformanceMetrics.DbTransactions | ||||||
|  | 		stats["total_queries"] = tm.metrics.PerformanceMetrics.DbQueries | ||||||
|  | 		stats["total_errors"] = tm.metrics.PerformanceMetrics.DbErrors | ||||||
|  | 		stats["avg_query_time"] = tm.metrics.PerformanceMetrics.DbQueryTime | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return stats | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 预定义的事务配置 | ||||||
|  |  | ||||||
|  | // GetDefaultTransactionConfig 获取默认事务配置 | ||||||
|  | func GetDefaultTransactionConfig() TransactionConfig { | ||||||
|  | 	return TransactionConfig{ | ||||||
|  | 		Timeout:        30 * time.Second, | ||||||
|  | 		RetryAttempts:  3, | ||||||
|  | 		RetryDelay:     100 * time.Millisecond, | ||||||
|  | 		IsolationLevel: "READ COMMITTED", | ||||||
|  | 		ReadOnly:       false, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetReadOnlyTransactionConfig 获取只读事务配置 | ||||||
|  | func GetReadOnlyTransactionConfig() TransactionConfig { | ||||||
|  | 	return TransactionConfig{ | ||||||
|  | 		Timeout:        10 * time.Second, | ||||||
|  | 		RetryAttempts:  1, | ||||||
|  | 		RetryDelay:     0, | ||||||
|  | 		IsolationLevel: "READ COMMITTED", | ||||||
|  | 		ReadOnly:       true, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetLongRunningTransactionConfig 获取长时间运行事务配置 | ||||||
|  | func GetLongRunningTransactionConfig() TransactionConfig { | ||||||
|  | 	return TransactionConfig{ | ||||||
|  | 		Timeout:        300 * time.Second, // 5分钟 | ||||||
|  | 		RetryAttempts:  5, | ||||||
|  | 		RetryDelay:     500 * time.Millisecond, | ||||||
|  | 		IsolationLevel: "REPEATABLE READ", | ||||||
|  | 		ReadOnly:       false, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetCriticalTransactionConfig 获取关键事务配置 | ||||||
|  | func GetCriticalTransactionConfig() TransactionConfig { | ||||||
|  | 	return TransactionConfig{ | ||||||
|  | 		Timeout:        60 * time.Second, | ||||||
|  | 		RetryAttempts:  5, | ||||||
|  | 		RetryDelay:     200 * time.Millisecond, | ||||||
|  | 		IsolationLevel: "SERIALIZABLE", | ||||||
|  | 		ReadOnly:       false, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 全局事务管理器实例 | ||||||
|  | var GlobalTransactionManager *TransactionManager | ||||||
|  |  | ||||||
|  | // InitTransactionManager 初始化事务管理器 | ||||||
|  | func InitTransactionManager(db *gorm.DB, metrics *MetricsCollector, config *OptimizedConfig) { | ||||||
|  | 	GlobalTransactionManager = NewTransactionManager(db, metrics, config) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetTransactionManager 获取全局事务管理器 | ||||||
|  | func GetTransactionManager() *TransactionManager { | ||||||
|  | 	return GlobalTransactionManager | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithDefaultTransaction 使用默认配置执行事务 | ||||||
|  | func WithDefaultTransaction(ctx context.Context, operation func(*TransactionContext) error) error { | ||||||
|  | 	if GlobalTransactionManager == nil { | ||||||
|  | 		return fmt.Errorf("事务管理器未初始化") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	config := GetDefaultTransactionConfig() | ||||||
|  | 	result := GlobalTransactionManager.WithTransaction(ctx, config, operation) | ||||||
|  | 	return result.Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithReadOnlyTransaction 使用只读配置执行事务 | ||||||
|  | func WithReadOnlyTransaction(ctx context.Context, operation func(*TransactionContext) error) error { | ||||||
|  | 	if GlobalTransactionManager == nil { | ||||||
|  | 		return fmt.Errorf("事务管理器未初始化") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	config := GetReadOnlyTransactionConfig() | ||||||
|  | 	result := GlobalTransactionManager.WithTransaction(ctx, config, operation) | ||||||
|  | 	return result.Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithCriticalTransaction 使用关键事务配置执行事务 | ||||||
|  | func WithCriticalTransaction(ctx context.Context, operation func(*TransactionContext) error) error { | ||||||
|  | 	if GlobalTransactionManager == nil { | ||||||
|  | 		return fmt.Errorf("事务管理器未初始化") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	config := GetCriticalTransactionConfig() | ||||||
|  | 	result := GlobalTransactionManager.WithTransaction(ctx, config, operation) | ||||||
|  | 	return result.Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SaveWithOptimisticLockGlobal 全局乐观锁保存 | ||||||
|  | func SaveWithOptimisticLockGlobal(ctx context.Context, model interface{}, condition string, args ...interface{}) error { | ||||||
|  | 	if GlobalTransactionManager == nil { | ||||||
|  | 		return fmt.Errorf("事务管理器未初始化") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return GlobalTransactionManager.SaveWithOptimisticLock(ctx, model, condition, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdateWithOptimisticLockGlobal 全局乐观锁更新 | ||||||
|  | func UpdateWithOptimisticLockGlobal(ctx context.Context, model interface{}, updates map[string]interface{}, condition string, args ...interface{}) error { | ||||||
|  | 	if GlobalTransactionManager == nil { | ||||||
|  | 		return fmt.Errorf("事务管理器未初始化") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return GlobalTransactionManager.UpdateWithOptimisticLock(ctx, model, updates, condition, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TransactionHelper 事务辅助函数集合 | ||||||
|  | type TransactionHelper struct { | ||||||
|  | 	tm *TransactionManager | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewTransactionHelper 创建事务辅助器 | ||||||
|  | func NewTransactionHelper(tm *TransactionManager) *TransactionHelper { | ||||||
|  | 	return &TransactionHelper{tm: tm} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SavePosition 保存持仓(带乐观锁) | ||||||
|  | func (th *TransactionHelper) SavePosition(ctx context.Context, position interface{}, userID, symbol string, version int) error { | ||||||
|  | 	condition := "user_id = ? AND symbol = ? AND version = ?" | ||||||
|  | 	return th.tm.SaveWithOptimisticLock(ctx, position, condition, userID, symbol, version) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SaveOrder 保存订单(带乐观锁) | ||||||
|  | func (th *TransactionHelper) SaveOrder(ctx context.Context, order interface{}, orderID string, version int) error { | ||||||
|  | 	condition := "order_id = ? AND version = ?" | ||||||
|  | 	return th.tm.SaveWithOptimisticLock(ctx, order, condition, orderID, version) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdateOrderStatus 更新订单状态(带乐观锁) | ||||||
|  | func (th *TransactionHelper) UpdateOrderStatus(ctx context.Context, orderID, newStatus string, version int) error { | ||||||
|  | 	updates := map[string]interface{}{ | ||||||
|  | 		"status":     newStatus, | ||||||
|  | 		"updated_at": time.Now(), | ||||||
|  | 	} | ||||||
|  | 	condition := "order_id = ? AND version = ?" | ||||||
|  | 	return th.tm.UpdateWithOptimisticLock(ctx, nil, updates, condition, orderID, version) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdatePositionQuantity 更新持仓数量(带乐观锁) | ||||||
|  | func (th *TransactionHelper) UpdatePositionQuantity(ctx context.Context, userID, symbol string, quantity float64, version int) error { | ||||||
|  | 	updates := map[string]interface{}{ | ||||||
|  | 		"quantity":   quantity, | ||||||
|  | 		"updated_at": time.Now(), | ||||||
|  | 	} | ||||||
|  | 	condition := "user_id = ? AND symbol = ? AND version = ?" | ||||||
|  | 	return th.tm.UpdateWithOptimisticLock(ctx, nil, updates, condition, userID, symbol, version) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetGlobalTransactionHelper 获取全局事务辅助器 | ||||||
|  | func GetGlobalTransactionHelper() *TransactionHelper { | ||||||
|  | 	if GlobalTransactionManager == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return NewTransactionHelper(GlobalTransactionManager) | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user