1、状态更新错误处理
This commit is contained in:
		| @ -253,3 +253,30 @@ func (e LineReversePosition) ClosePositionBatch(c *gin.Context) { | ||||
| 	} | ||||
| 	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-已平仓"` | ||||
| 	AveragePrice        decimal.Decimal `json:"averagePrice" 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.ControlBy | ||||
| } | ||||
|  | ||||
| @ -22,6 +22,9 @@ func registerLineReversePositionRouter(v1 *gin.RouterGroup, authMiddleware *jwt. | ||||
| 		r.GET("/:id", actions.PermissionAction(), api.Get) | ||||
| 		r.POST("", api.Insert) | ||||
| 		r.PUT("/:id", actions.PermissionAction(), api.Update) | ||||
|  | ||||
| 		//清理所有 | ||||
| 		r.DELETE("/clean-all", actions.PermissionAction(), api.CleanAll) | ||||
| 		r.DELETE("", api.Delete) | ||||
|  | ||||
| 		r.PUT("close/:id", actions.PermissionAction(), api.ClosePosition) | ||||
|  | ||||
| @ -25,6 +25,27 @@ type LineReversePosition struct { | ||||
| 	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 { | ||||
| 	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/shopspring/decimal v1.2.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/gin-swagger v1.6.0 | ||||
| 	github.com/swaggo/swag v1.16.2 | ||||
| @ -82,6 +83,7 @@ require ( | ||||
| 	github.com/cloudwego/iasm v0.2.0 // indirect | ||||
| 	github.com/coreos/go-semver v0.3.1 // 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/fatih/color v1.15.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/nyaruka/phonenumbers v1.0.55 // 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/prometheus/client_model v0.5.0 // indirect | ||||
| 	github.com/prometheus/common v0.45.0 // indirect | ||||
|  | ||||
| @ -16,7 +16,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| 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{}) | ||||
| 	var apiUserInfo models.LineApiUser | ||||
| 	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 { | ||||
| 			case mainOrder.PositionSide == "LONG" && mainOrder.Side == "BUY", mainOrder.PositionSide == "SHORT" && mainOrder.Side == "SELL": | ||||
| 				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 | ||||
| 					} | ||||
| 				} | ||||
| 			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 _, _, 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 | ||||
| 					} | ||||
| 				} | ||||
| @ -307,6 +309,20 @@ func (e *ReverseService) SaveMainOrder(mapData map[string]interface{}, apiInfo D | ||||
| 	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信息 | ||||
| // return | ||||
| @ -364,25 +380,27 @@ func (e *ReverseService) savePosition(order *DbModels.LineReverseOrder, apiInfo | ||||
| 		//平仓 | ||||
| 		if closePosition { | ||||
| 			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 { | ||||
| 			//只减仓 | ||||
| 			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 { | ||||
| 			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 { | ||||
| 		querySql = "reverse_api_id =? and position_side =? and symbol =? and reverse_status in (0,1)" | ||||
| 		//减仓 判断是否为平仓 | ||||
| 		closePosition = e.getClosePosition(reducePosition, apiInfo, order, order.PositionSide, isMain) | ||||
|  | ||||
| 		// 关键修复:确保反向持仓状态正确更新 | ||||
| 		if closePosition { | ||||
| 			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 { | ||||
| 			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 { | ||||
| 			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 | ||||
|  | ||||
| 	err := e.Orm.Transaction(func(tx *gorm.DB) error { | ||||
| 		err1 := tx.Model(&position).Where(querySql, | ||||
| 			order.ApiId, positionSide, order.Symbol).First(&position).Error | ||||
| 			err1 := tx.Model(&position).Where(querySql, | ||||
| 				order.ApiId, positionSide, order.Symbol).First(&position).Error | ||||
|  | ||||
| 		if err1 != nil { | ||||
| 			//主单仓位不存在,创建新仓位 | ||||
| 			if isMain && errors.Is(err1, gorm.ErrRecordNotFound) && !reducePosition { | ||||
| 			if err1 != nil { | ||||
| 				//主单仓位不存在,创建新仓位 | ||||
| 				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 { | ||||
| 					return err2 | ||||
| 					averagePrice = position.AveragePrice | ||||
| 				} else { | ||||
| 					return err1 | ||||
| 				} | ||||
|  | ||||
| 				averagePrice = position.AveragePrice | ||||
| 			} else { | ||||
| 				return err1 | ||||
| 			} | ||||
| 		} else { | ||||
| 			var totalAmount decimal.Decimal | ||||
| 			var totalPrice decimal.Decimal | ||||
| 			if !position.Amount.IsZero() && !position.AveragePrice.IsZero() { | ||||
| @ -457,16 +476,17 @@ func (e *ReverseService) savePosition(order *DbModels.LineReverseOrder, apiInfo | ||||
| 			return err2 | ||||
| 		} | ||||
|  | ||||
| 		dbResult := tx.Exec(sqlStr, sql.Named("totalNum", totalNum), sql.Named("id", position.Id), sql.Named("averagePrice", averagePrice)) | ||||
| 		if dbResult.Error != nil { | ||||
| 			return dbResult.Error | ||||
| 		} | ||||
| 		// 使用乐观锁执行更新 | ||||
| 			dbResult := tx.Exec(sqlStr, sql.Named("totalNum", totalNum), sql.Named("id", position.Id), sql.Named("averagePrice", averagePrice), sql.Named("version", position.Version)) | ||||
| 			if dbResult.Error != nil { | ||||
| 				return dbResult.Error | ||||
| 			} | ||||
|  | ||||
| 		order.PositionId = position.Id | ||||
| 		if dbResult.RowsAffected == 0 { | ||||
| 			e.Log.Errorf("减仓数据 是否平仓单:%v :%v", closePosition, order) | ||||
| 			return errors.New("没有找到对应的持仓信息") | ||||
| 		} | ||||
| 			order.PositionId = position.Id | ||||
| 			if dbResult.RowsAffected == 0 { | ||||
| 				e.Log.Errorf("减仓数据 是否平仓单:%v :%v, 可能存在并发冲突", closePosition, order) | ||||
| 				return fmt.Errorf("没有找到对应的持仓信息或版本冲突,position_id:%d, version:%d", position.Id, position.Version) | ||||
| 			} | ||||
|  | ||||
| 		if reducePosition && !isMain { | ||||
| 			remainQuantity = position.ReverseAmount.Sub(totalNum) | ||||
| @ -1052,145 +1072,3 @@ func getOrderType(t string) int { | ||||
| 	} | ||||
| 	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