package data

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/shopspring/decimal"
	"matchmaking-system/internal/biz/structure"
	"matchmaking-system/internal/data/memory"
	"matchmaking-system/internal/data/mq/consumer"
	"matchmaking-system/internal/data/socket/forexData"
	"matchmaking-system/internal/data/socket/publicData"
	"matchmaking-system/internal/data/tradedeal/virtual"
	"matchmaking-system/internal/pkg/flags"
	"matchmaking-system/internal/pkg/logging/applogger"
	"matchmaking-system/internal/pkg/logging/common"
	"matchmaking-system/internal/pkg/setting"
	"strconv"
	"sync"
	"time"

	forexd "matchmaking-system/internal/data/tradedeal/forex"
)

/*
外汇行情订阅
1、初始化现货订阅信息
2、接收用户现货下单的交易对
3、根据交易对订阅现货行情数据
4、写入内存中用于并发计算
5、记录在热缓存中
*/

// QuitesForex
// @Description:
type QuitesForex struct {
	Ev string  `json:"ev"` // 事件类型
	P  string  `json:"p"`  // 交易对
	A  float64 `json:"a"`  // 买价
	X  int     `json:"x"`  // 交易所标识符
	B  float64 `json:"b"`  // 卖价
	T  int64   `json:"t"`  // 时间
}
type ForexJsonData struct {
	Event     string  `json:"ev"`   // 事件类型(实时数据)
	Pair      string  `json:"pair"` // 货币对
	Open      float64 `json:"o"`    // 开盘价
	Close     float64 `json:"c"`    // 收盘价
	High      float64 `json:"h"`    // 最高价
	Low       float64 `json:"l"`    // 最低价
	Volume    int     `json:"v"`    // 交易量
	Timestamp int64   `json:"s"`    // 时间戳
}

// ForexSymbol
// @Description:
type ForexSymbol struct {
	ForexMap       chan []byte // 外汇订单交易对 -- 用户外汇下单通知订阅
	ForexMapSymbol sync.Map    // 外汇订阅交易对簿
}

// InitCacheSymbolForex
//
//	@Description:
//	@param ctx
//	@param uo
func InitCacheSymbolForex(ctx context.Context, uo *Data) {
	contractList, err := GetBotForexList(ctx, uo)
	if err != nil {
		return
	}

	for _, value := range contractList {
		// Write to the redis list hot cache for initializing subscriptions when the program is pulled up
		checkBool, err := uo.redisDB.HExists(context.Background(), setting.MarketForex, value).Result()
		if err != nil {
			applogger.Error("%v InitCacheSymbolSpots.MarketForex.HExists:%v", common.ErrForex, err)
			return
		}
		applogger.Info("初始化外汇交易对:%v", value)

		if !checkBool {
			if err = uo.redisDB.HSet(context.Background(), setting.MarketForex, value, value).Err(); err != nil {
				applogger.Error("%v InitCacheSymbolSpots.HSet:%v", common.ErrForex, err)
				return
			}
		}
	}
}

// InitSubscribeQuotesForex
//
//	@Description:
//	@param ctx
//	@param uo
//	@param contract
func InitSubscribeQuotesForex(contract *ForexSymbol) {
	for {
		listSpots, err := LoadLRangeList(setting.MarketForex)
		if err != nil {
			applogger.Error("%v InitSubscribeQuotesForex.LoadLRangeList:%v", common.ErrForex, err)
			return
		}

		for _, value := range listSpots {
			// Prevent duplicate subscription to CtrIp
			_, ok := contract.ForexMapSymbol.Load(value)
			if ok {
				continue
			}

			contract.ForexMap <- []byte(value)
		}

		time.Sleep(10 * time.Second)
	}
}

// SubscribeQuotesForex
//
//	@Description: 外汇订阅
//	@param ctx
//	@param uo
//	@param contract
func SubscribeQuotesForex(ctx context.Context, uo *Data, forex *ForexSymbol) {
	for {
		select {
		case symbol, _ := <-forex.ForexMap:
			symbolStr := string(symbol)

			// Prevent duplicate subscription to CtrIp
			_, ok := forex.ForexMapSymbol.Load(symbolStr)
			if ok {
				time.Sleep(5 * time.Second)
				continue
			}

			// Write to the redis list hot cache for initializing subscriptions when the program is pulled up
			checkBool, err := uo.redisDB.HExists(context.Background(), setting.MarketForex, symbolStr).Result()
			if err != nil {
				applogger.Error("%v SubscribeQuotesForex.MarketForex.HExists:%v", common.ErrForex, err)
				time.Sleep(5 * time.Second)
				continue
			}
			if !checkBool {
				if err = uo.redisDB.HSet(context.Background(), setting.MarketForex, symbolStr, symbolStr).Err(); err != nil {
					applogger.Error("%v SubscribeQuotesForex.MarketForex.HSet:%v", common.ErrForex, err)
					time.Sleep(5 * time.Second)
					continue
				}
			}

			// 订阅买一卖一报价
			go func() {
				topIc := fmt.Sprintf("%v.LastForex", symbolStr)
				pubSub := uo.redisDB.Subscribe(ctx, topIc)
				defer pubSub.Close()
				if _, err = pubSub.Receive(ctx); err != nil {
					applogger.Error("%v SubscribeQuotesForex.Receive:%v", common.ErrForex, err)
					return
				}
				chp := pubSub.Channel()
				for msg := range chp {
					var subMsg QuitesForex
					if err = json.Unmarshal([]byte(msg.Payload), &subMsg); err != nil {
						applogger.Error("%v SubscribeQuotesForex.QuitesForex.Unmarshal:%v", common.ErrForex, err)
						close(forex.ForexMap)
						return
					}
					// 交易类型:0最新价,1买入,2卖出
					askPriceKey := publicData.SymbolCache(flags.Wh, symbolStr, flags.TradeTypeBuy)
					askPrice := strconv.FormatFloat(subMsg.A, 'g', -1, 64)
					if err = memory.ForexCache.Set(askPriceKey, []byte(askPrice)); err != nil {
						applogger.Error("%v askPriceKey Set:%v", common.ErrForex, err)
						continue
					}
					bidPriceKey := publicData.SymbolCache(flags.Wh, symbolStr, flags.TradeTypeSell)
					bidPrice := strconv.FormatFloat(subMsg.B, 'g', -1, 64)
					if err = memory.ForexCache.Set(bidPriceKey, []byte(bidPrice)); err != nil {
						applogger.Error("%v bidPriceKey Set:%v", common.ErrForex, err)
						continue
					}
					if !flags.CheckSetting {
						applogger.Info("当前写入外汇卖一买一最新报价:%v,%v,%v", symbolStr, askPrice, bidPrice)
					}
				}
			}()

			// 订阅实时报价
			go func() {
				topIc := fmt.Sprintf("%v.Forex", symbolStr)
				pubSub := uo.redisDB.Subscribe(ctx, topIc)
				defer pubSub.Close()
				if _, err = pubSub.Receive(ctx); err != nil {
					applogger.Error("%v SubscribeQuotesForex.Receive:%v", common.ErrForex, err)
					return
				}
				chp := pubSub.Channel()
				for msg := range chp {
					var subMsg ForexJsonData
					if err = json.Unmarshal([]byte(msg.Payload), &subMsg); err != nil {
						applogger.Error("%v SubscribeQuotesForex.QuitesForex.Unmarshal:%v", common.ErrForex, err)
						close(forex.ForexMap)
						return
					}
					// 交易类型:0最新价,1买入,2卖出
					priceKey := publicData.SymbolCache(flags.Wh, symbolStr, flags.TradeTypePrice)
					price := strconv.FormatFloat(subMsg.Close, 'g', -1, 64)
					if err = memory.ForexImmediateCache.Set(priceKey, []byte(price)); err != nil {
						applogger.Error("%v priceKey Set:%v", common.ErrForex, err)
						continue
					}
					if !flags.CheckSetting {
						applogger.Info("当前写入外汇即时最新报价:%v,%v,%v", symbolStr, priceKey, price)
					}
				}
			}()

			// Write in the map to determine whether to repeat subscription
			forex.ForexMapSymbol.Store(symbolStr, symbolStr)
		}
	}
}

// OrderSubAdminForexSubscribeBySum
//
//	@Description: 持仓订单浮动盈亏计算
//	@param ctx
//	@param uo
func OrderSubAdminForexSubscribeBySum(uo *Data) {
	for {
		topIc := setting.AdminForexSubscribe
		hashMap, err := uo.redisDB.HGetAll(context.Background(), topIc).Result()
		if err != nil {
			applogger.Error("orderSubAdminForexSubscribe.HGetAll:%v", err)
			time.Sleep(5 * time.Second)
			continue
		}
		var hashList []forexd.ForexTallyCache
		for _, value := range hashMap {
			var msg forexd.ForexTallyCache
			if err = json.Unmarshal([]byte(value), &msg); err != nil {
				time.Sleep(5 * time.Second)
				continue
			}
			switch msg.Status {
			case flags.Entrust: // 挂单
			case flags.Position: // 持仓
				hashList = append(hashList, msg)
			default: // 平仓|撤单
				err = uo.redisDB.HDel(context.Background(), topIc, msg.OrderId).Err()
				if err != nil {
					applogger.Error("AdminForexSubscribe.HDel:%v", err)
					continue
				}
			}
		}
		applogger.Info("外汇数据量统计:%v", len(hashList))
		// 统计外汇总持仓订单浮动盈亏
		priceSum := decimal.Zero
		for _, value := range hashList {
			orderModel := forexData.ForexOrderProcessing(topIc, value)
			if orderModel != nil {
				var subPrice decimal.Decimal
				newPrice, err := decimal.NewFromString(orderModel.Price)
				if err != nil {
					continue
				}
				openPrice, err := decimal.NewFromString(orderModel.OpenPrice)
				if err != nil {
					continue
				}
				switch value.Order.TradeType {
				case flags.TradeTypeBuy: // 买涨 = 新价格 - 开仓价
					subPrice = newPrice.Sub(openPrice)
				case flags.TradeTypeSell: // 卖跌 = 开仓价 - 新价格
					subPrice = openPrice.Sub(newPrice)
				default:
					subPrice = decimal.Zero
				}
				orderNumber := decimal.RequireFromString(value.Order.OrderNumber)
				pryNum := decimal.RequireFromString(value.Order.PryNum)
				price := subPrice.Mul(orderNumber).Mul(pryNum) // 外汇浮动盈亏计算
				priceSum = priceSum.Add(price)                 // 累加盈亏统计
			}
		}
		// 写入缓存
		if err = memory.ForexFloating.Set(flags.FloatingWh, []byte(priceSum.String())); err != nil {
			applogger.Error("统计外汇持仓订单浮动盈亏错误:%v", err)
			time.Sleep(5 * time.Second)
			continue
		}
		applogger.Info("统计外汇持仓订单浮动盈亏:%v", priceSum)

		time.Sleep(600 * time.Millisecond)
	}
}

// ForexTransactionEntrust
//
//	@Description: 外汇委托订单
//	@param ctx
func ForexTransactionEntrust(ctx context.Context) {
	for {
		ForexTransactionCalculationEntrust(ctx)
		time.Sleep(400 * time.Millisecond)
	}
}

// ForexTransactionCalculationEntrust
//
//	@Description:
//	@param ctx
func ForexTransactionCalculationEntrust(ctx context.Context) {
	var wg sync.WaitGroup

	entrustList, err := Reds.HGetAll(context.Background(), setting.MarketForexEntrust).Result()
	if err != nil {
		applogger.Error("%v ForexTransactionCalculationEntrust.HGetAll:%v", common.ErrForex, err)
		return
	}

	for _, value := range entrustList {
		var msg = make(chan forexd.ForexTallyCache, 1)
		var entrust forexd.ForexTallyCache
		if err = json.Unmarshal([]byte(value), &entrust); err != nil {
			applogger.Error("%v ForexTransactionCalculationEntrust.Unmarshal:%v", common.ErrForex, err)
			continue
		}

		var newPrice decimal.Decimal
		newPrice, err = GetForexPrice(entrust.Symbol, entrust.Order.TradeType)
		if err != nil {
			applogger.Error("%v ForexTransactionCalculationPosition.GetForexPrice:%v", common.ErrForex, err)
			continue
		}

		wg.Add(1)
		go func(value string) {
			resultMsg := ForexConcurrentComputingEntrust(&entrust, newPrice, &wg)
			msg <- resultMsg // 通知管道异步处理订单操作
		}(value)

		// 处理并发结果(需要改成通过信道通知)
		select {
		case resultMsg, _ := <-msg:
			switch resultMsg.Status {
			case flags.Entrust: // 挂单中
				if !flags.CheckSetting {
					applogger.Info("从信道接收外汇挂单信息:%v", resultMsg)
				}
			case flags.Position: // 持仓中
				if !flags.CheckSetting {
					applogger.Info("从信道接收到外汇持仓信息:%v", resultMsg)
				}

				// 处理开仓逻辑
				if err = ForexLiquidationEntrust(ctx, &resultMsg); err != nil {
					applogger.Error("%v ForexTransactionCalculationEntrust.ForexLiquidationEntrust:%v", common.ErrForex, err)
					continue
				}
				// 写入持仓缓存列表
				data, err := json.Marshal(resultMsg)
				if err != nil {
					applogger.Error("%v ForexTransactionCalculationEntrust.Marshal:%v", common.ErrForex, err)
					continue
				}

				// TODO: 写入持仓消息列表
				//if err = uo.mqProducer.Entrust.PushNotice(data); err != nil {
				//	applogger.Error("%v ForexTransactionCalculationEntrust.PushNotice:%v", common.ForexError, err)
				//	continue
				//}

				// 写入外汇持仓缓存队列
				if err = Reds.HSet(context.Background(), setting.MarketForexPosition, resultMsg.OrderId, string(data)).Err(); err != nil {
					applogger.Error("%v ForexTransactionCalculationEntrust.MarketForexPosition.HSet:%v", common.ErrForex, err)
					continue
				}
			}
		}
	}

	wg.Wait()
	applogger.Info("外汇挂单并发计算执行完毕......")
}

// ForexConcurrentComputingEntrust
//
//	@Description: 外汇挂单缓存列表业务处理
//	@param order
//	@param orderOld
//	@param newPrice
//	@param bidPrice
//	@param askPrice
//	@param wg
//	@return tradedeal.ForexTallyCache
func ForexConcurrentComputingEntrust(order *forexd.ForexTallyCache, newPrice decimal.Decimal, wg *sync.WaitGroup) forexd.ForexTallyCache {
	var deleteCache bool
	var dealPrice string
	var limitPrice = decimal.RequireFromString(order.Order.LimitPrice) // 限价-价格
	var status = order.Status                                          // 原始价格

	switch order.Order.TradeType {
	case flags.TradeTypeBuy: // 买涨
		if !flags.CheckSetting {
			applogger.Info("用户ID:%v,限价买入下单价格:%v,最新市价:%v,判定结果:%v,原始状态:%v", order.UserId, limitPrice, newPrice, limitPrice.Cmp(newPrice), order.Status)
		}
		// 限价买入逻辑判定 -> 挂单金额 > 当前价格
		if limitPrice.Cmp(newPrice) > 0 {
			deleteCache = true
			dealPrice = limitPrice.String() // 开仓价格
		}
	case flags.TradeTypeSell: // 买跌
		if !flags.CheckSetting {
			applogger.Info("用户ID:%v,限价卖出下单价格:%v,最新市价:%v,判定结果:%v,原始状态:%v", order.UserId, limitPrice, newPrice, limitPrice.Cmp(newPrice), order.Status)
		}
		// 限价卖入逻辑判定 -> 挂单金额 < 当前价格
		if limitPrice.Cmp(newPrice) < 0 {
			deleteCache = true
			dealPrice = limitPrice.String() // 开仓价格
		}
	default:
	}

	if deleteCache {
		order.Status = flags.Position
		// 删除挂单缓存列表
		if err := Reds.HDel(context.Background(), setting.MarketForexEntrust, order.OrderId).Err(); err != nil {
			applogger.Error("%v ForexConcurrentComputingEntrust.MarketForexEntrust.HDel:%v", common.ErrForex, err)
			order.Status = status
			dealPrice = decimal.Zero.String()
		}
	}

	wg.Done()
	return forexd.ForexTallyCache{
		UserId:    order.UserId,  // 用户Id
		OrderId:   order.OrderId, // 订单Id
		Symbol:    order.Symbol,  // 下单交易对
		OpenPrice: dealPrice,     // 开仓价格
		Status:    order.Status,  // 订单状态
		Order:     order.Order,   // 下单信息
	}
}

// ForexMqOpenBusiness
//
//	@Description: TODO: 外汇持仓消息队列
//	@param ctx
//	@param uo
func ForexMqOpenBusiness(ctx context.Context, uo *Data) {
	go func() {
		uo.mqConsumer.Entrust.ConsumerNotice()
	}()
	for {
		select {
		case message, ok := <-consumer.ConsumerEntrustMq.ForexMq:
			if !ok {
				time.Sleep(5 * time.Second)
				applogger.Error("%v ForexMqOpenBusiness.ForexMq err....", common.ErrForex)
				continue
			}

			// 持仓订单缓存数据序列化解析
			var resultMsg forexd.ForexTallyCache
			if err := json.Unmarshal(message, &resultMsg); err != nil {
				applogger.Error("%v ForexMqOpenBusiness.Unmarshal:%v", common.ErrForex, err)
				time.Sleep(5 * time.Second)
				continue
			}

			// 持仓平仓
			if err := ForexLiquidationEntrust(ctx, &resultMsg); err != nil {
				applogger.Error("%v ForexMqOpenBusiness.ForexLiquidationEntrust:%v", common.ErrForex, err)
				continue
			}
		default:
			// TODO: 是否处理空队列缓存的情况
			applogger.Error("这里没有监控到持仓mq队列消息.........")
			time.Sleep(2 * time.Second)
		}
	}
}

// ForexLiquidationEntrust
//
//	@Description: 外汇挂单-->开仓业务处理
//	@param ctx
//	@param order
//	@return error
func ForexLiquidationEntrust(ctx context.Context, order *forexd.ForexTallyCache) error {
	// 1、外汇开仓操作
	err := ForexOpenPosition(ctx, Msql, order.UserId, order.OrderId, order.OpenPrice, order.Order)
	if err != nil {
		applogger.Error("%v ForexLiquidationEntrust.ForexOpenPosition:%v", common.ErrForex, err)
		return err
	}

	// 2、开仓更新用户外汇订阅缓存订单状态
	userSubKey := virtual.OrderIdListKey(setting.ForexSubscribe, order.UserId)
	if err = UpdateForexSubscribeHashStatusByOrderId(order.OrderId, userSubKey, flags.Position, order.ClosingPrice); err != nil {
		applogger.Error("%v ForexLiquidationEntrust.ForexSubscribe:%v", common.ErrForex, err)
		return err
	}

	// 3、开仓更新管理员外汇订阅缓存订单状态
	if err = UpdateForexSubscribeHashStatusByOrderId(order.OrderId, setting.AdminForexSubscribe, flags.Position, order.ClosingPrice); err != nil {
		applogger.Error("%v ForexLiquidationEntrust.AdminForexSubscribe:%v", common.ErrForex, err)
		return err
	}

	return nil
}

// ForexTransactionPosition
//
//	@Description: 外汇-持仓业务处理
//	@param ctx
func ForexTransactionPosition(ctx context.Context) {
	for {
		ForexTransactionCalculationPosition(ctx)
		time.Sleep(400 * time.Millisecond)
	}
}

// ForexTransactionCalculationPosition
//
//	@Description: 监控外汇持仓缓存列表(注意:止盈止损|强行平仓)
//	@param ctx
func ForexTransactionCalculationPosition(ctx context.Context) {
	var wg sync.WaitGroup

	entrustList, err := Reds.HGetAll(context.Background(), setting.MarketForexPosition).Result()
	if err != nil {
		applogger.Error("%v ForexTransactionCalculationPosition.LRange:%v", common.ErrForex, err)
		return
	}

	for _, value := range entrustList {
		var msg = make(chan forexd.ForexTallyCache, 1)
		var entrust forexd.ForexTallyCache
		if err = json.Unmarshal([]byte(value), &entrust); err != nil {
			applogger.Error("%v ForexTransactionCalculationPosition.Unmarshal:%v", common.ErrForex, err)
			continue
		}

		var newPrice decimal.Decimal
		newPrice, err = GetForexPriceNew(entrust.Symbol, entrust.Order.TradeType)
		if err != nil {
			applogger.Warn("%v ForexTransactionCalculationPosition.GetForexPrice:%v", common.ErrForex, err)
			continue
		}

		wg.Add(1)
		go func(value string) {
			resultMsg := ForexConcurrentComputingPosition(&entrust, newPrice, &wg)
			msg <- resultMsg
		}(value)

		// 处理并发结果
		select {
		case resultMsg, _ := <-msg:
			switch resultMsg.Status {
			case flags.Position: // 持仓
				if !flags.CheckSetting {
					applogger.Info("从信道接收到外汇持仓信息:%v", resultMsg)
				}
			case flags.Close: // 平仓
				if !flags.CheckSetting {
					applogger.Info("从信道接收到外汇平仓信息:%v", resultMsg)
				}

				// TODO: 平仓操作(优化写入队列中等待平仓)
				//var byteStr []byte
				//byteStr, err = json.Marshal(resultMsg)
				//if err != nil {
				//	applogger.Error("%v ForexTransactionCalculationPosition.Marshal:%v", common.ForexError, err)
				//	continue
				//}
				//if err = uo.mqProducer.Position.PushNotice(byteStr); err != nil {
				//	applogger.Error("%v ForexTransactionCalculationPosition.PushNotice:%v", common.ForexError, err)
				//	continue
				//}

				if err = ForexLiquidationPosition(ctx, &resultMsg); err != nil {
					applogger.Error("%v ForexTransactionCalculationPosition.ForexLiquidationPosition:%v", common.ErrForex, err)
					continue
				}
			}
		}
	}

	wg.Wait()
	applogger.Info("外汇持仓并发计算执行完毕......")
}

// ForexConcurrentComputingPosition
//
//	@Description: 外汇持仓缓存列表业务处理
//	@param order
//	@param newPrice
//	@param wg
//	@return tradedeal.ForexTallyCache
func ForexConcurrentComputingPosition(order *forexd.ForexTallyCache, newPrice decimal.Decimal, wg *sync.WaitGroup) forexd.ForexTallyCache {
	var deleteCache, checkBool bool
	var dealPrice string
	var sellShort decimal.Decimal
	var openPrice = decimal.RequireFromString(order.OpenPrice)           // 限价
	var orderNumber = decimal.RequireFromString(order.Order.OrderNumber) // 仓位
	var status = order.Status                                            // 原始状态
	var pryNum = decimal.RequireFromString(order.Order.PryNum)           // 杠杆

	// 下单判定设置(false无设置|true止盈止损)
	_, stopWinPrice, stopLossPrice, _ := ForexVoteStopType(order.Order)

	switch order.Order.TradeType {
	case flags.TradeTypeBuy: // 买涨(强平)
		if !flags.CheckSetting {
			applogger.Info("用户ID:%v,持仓开仓价格:%v,最新市价:%v,止盈:%v,止损:%v,原始状态:%v", order.UserId, openPrice, newPrice, stopWinPrice, stopLossPrice, order.Status)
		}
		// 买涨挂止损止盈,止盈价必须<当前价,止损价必须>当前价;
		if !stopWinPrice.IsZero() {
			if stopWinPrice.Cmp(newPrice) < 0 {
				checkBool = true
				dealPrice = newPrice.String() // 平仓价格
			}
		}
		if !stopLossPrice.IsZero() {
			if stopLossPrice.Cmp(newPrice) > 0 {
				checkBool = true
				dealPrice = newPrice.String() // 平仓价格
			}
		}
		// 强行平仓(做多)
		if !checkBool {
			sellShort = newPrice.Sub(openPrice).Mul(orderNumber).Mul(order.Order.System.FaceValue).Mul(pryNum)
		}
	case flags.TradeTypeSell: // 买跌(存在强行平仓)
		if !flags.CheckSetting {
			applogger.Info("用户ID:%v,限价持仓买跌下单价格:%v,最新市价:%v,止盈:%v,止损:%v,原始状态:%v", order.UserId, openPrice, newPrice, stopWinPrice, stopLossPrice, order.Status)
		}
		// 买跌挂止损止盈,止盈价必须>当前价,止损价必须<当前价;
		if !stopWinPrice.IsZero() {
			if stopWinPrice.Cmp(newPrice) > 0 {
				checkBool = true
				dealPrice = newPrice.String() // 平仓价格
			}
		}
		if !stopLossPrice.IsZero() {
			if stopLossPrice.Cmp(newPrice) < 0 {
				checkBool = true
				dealPrice = newPrice.String() // 平仓价格
			}
		}
		// 强行平仓(做空)
		if !checkBool {
			sellShort = openPrice.Sub(newPrice).Mul(orderNumber).Mul(order.Order.System.FaceValue).Mul(pryNum)
		}
	default:
	}
	/* 强行平仓(做多|做空)
	1、做多:(最新成交价-开仓成交价)*仓位
	1、做空:(开仓成交价-最新成交价)*仓位
	2、当浮动盈亏 >= 保证金的70%订单强行平仓
	*/
	if sellShort.IsNegative() {
		orderAmount := decimal.RequireFromString(order.Order.EarnestMoney)
		floatPrice := orderAmount.Mul(order.Order.System.CompelNum)
		if !flags.CheckSetting {
			applogger.Info("强平保证金:%v,强平浮动70%:%v,强行平仓判定:%v", orderAmount, floatPrice, sellShort.Abs().Cmp(floatPrice))
		}
		if sellShort.Abs().Cmp(floatPrice) >= 0 {
			checkBool = true
			dealPrice = newPrice.String()
		}
	}
	// 判定订单状态
	if checkBool {
		deleteCache = true
		order.Status = flags.Close
	}

	if deleteCache {
		// 删除持仓缓存列表数据
		if err := Reds.HDel(context.Background(), setting.MarketForexPosition, order.OrderId).Err(); err != nil {
			applogger.Error("%v ForexConcurrentComputingPosition.MarketForexPosition.HDel:%v", common.ErrForex, err)
			order.Status = status
		}
	}

	wg.Done()
	return forexd.ForexTallyCache{
		UserId:       order.UserId,  // 用户Id
		OrderId:      order.OrderId, // 订单Id
		Symbol:       order.Symbol,  // 下单交易对
		ClosingPrice: dealPrice,     // 平仓价格
		Status:       order.Status,  // 订单状态
		Order:        order.Order,   // 下单信息
	}
}

// ForexMqClosingBusiness
//
//	@Description: TODO: 处理外汇平仓消息队列
//	@param ctx
//	@param uo
func ForexMqClosingBusiness(ctx context.Context, uo *Data) {
	go func() {
		uo.mqConsumer.Position.ConsumerNotice()
	}()
	for {
		select {
		case message, ok := <-consumer.ConsumerPositionMq.ForexMq:
			if !ok {
				time.Sleep(5 * time.Second)
				applogger.Error("%v ForexMqClosingBusiness.ForexMq err....", common.ErrForex)
				continue
			}
			applogger.Info("接收到平仓信息:%v", string(message))

			// 持仓订单缓存数据序列化解析
			var resultMsg forexd.ForexTallyCache
			if err := json.Unmarshal(message, &resultMsg); err != nil {
				applogger.Error("%v ForexMqClosingBusiness.Unmarshal:%v", common.ErrForex, err)
				time.Sleep(5 * time.Second)
				continue
			}

			// 持仓平仓
			if err := ForexLiquidationPosition(ctx, &resultMsg); err != nil {
				applogger.Error("%v ForexMqClosingBusiness.ForexLiquidationPosition:%v", common.ErrForex, err)
				continue
			}
		default:
			// TODO: 是否处理空队列缓存的情况
			time.Sleep(2 * time.Second)
		}
	}
}

// ForexLiquidationPosition
//
//	@Description: 外汇平仓操作
//	@param ctx
//	@param order
//	@return error
func ForexLiquidationPosition(ctx context.Context, order *forexd.ForexTallyCache) error {
	// 1、平仓操作
	err := ForexClosingPosition(ctx, Msql, order.OrderId, order.ClosingPrice, order.Order)
	if err != nil {
		applogger.Error("%v ForexLiquidationPosition.ForexClosingPosition:%v", common.ErrForex, err)
		return err
	}

	// 2、平仓更新用户外汇订阅订单状态
	userSubKey := virtual.OrderIdListKey(setting.ForexSubscribe, order.UserId)
	if err = UpdateForexSubscribeHashStatusByOrderId(order.OrderId, userSubKey, flags.Close, order.ClosingPrice); err != nil {
		applogger.Error("%v ForexLiquidationPosition.ForexSubscribe:%v", common.ErrForex, err)
		return err
	}

	// 3、平仓更新管理员外汇订阅订单状态
	if err = UpdateForexSubscribeHashStatusByOrderId(order.OrderId, setting.AdminForexSubscribe, flags.Close, order.ClosingPrice); err != nil {
		applogger.Error("%v ForexLiquidationPosition.AdminForexSubscribe:%v", common.ErrForex, err)
		return err
	}

	return nil
}

// ForexVoteStopType
//
//	@Description: 设置外汇止盈止损
//	@param order
//	@return bool
//	@return decimal.Decimal
//	@return decimal.Decimal
//	@return error
func ForexVoteStopType(order structure.ForexOrder) (bool, decimal.Decimal, decimal.Decimal, error) {
	var checkBool bool
	var stopWinPrice, stopLossPrice decimal.Decimal
	switch order.StopType {
	case flags.StopTypeNone: // 暂无设置止盈止损
		checkBool = false
	case flags.StopTypeSet: // 设置止盈止损
		checkBool = true
		if len(order.StopWinPrice) != 0 {
			stopWinPrice = decimal.RequireFromString(order.StopWinPrice)
		}
		if len(order.StopLossPrice) != 0 {
			stopLossPrice = decimal.RequireFromString(order.StopLossPrice)
		}
	default:
		return checkBool, stopWinPrice, stopLossPrice, flags.ErrContractThirteen
	}

	return checkBool, stopWinPrice, stopLossPrice, nil
}

// GetForexPrice
//
//	@Description: 外汇交易对卖一买一最新报价
//	@param symbol
//	@return decimal.Decimal
//	@return decimal.Decimal
//	@return decimal.Decimal
//	@return error
func GetForexPrice(symbol string, tradeType int64) (decimal.Decimal, error) {
	var err error
	var newPrice []byte
	var cacheKey string

	switch tradeType {
	case 1: // 外汇交易对买一最新报价
		cacheKey = publicData.SymbolCache(flags.Wh, symbol, flags.TradeTypeBuy)
		newPrice, err = memory.GetForexCache(cacheKey)
	case 2: // 外汇交易对卖一最新报价
		cacheKey = publicData.SymbolCache(flags.Wh, symbol, flags.TradeTypeSell)
		newPrice, err = memory.GetForexCache(cacheKey)
	}
	if err != nil {
		applogger.Error("%v GetForexPrice.Get:%v", common.ErrForex, err)
		return decimal.Decimal{}, err
	}

	priceList, err := decimal.NewFromString(string(newPrice))
	if err != nil {
		applogger.Error("%v ForexTransactionCalculationPosition.Get:%v", common.ErrForex, err)
		return decimal.Decimal{}, err
	}

	applogger.Info("ForexCode:%v,topIc:%v,ForexPrice:%v", symbol, cacheKey, priceList)

	return priceList, nil
}
func GetForexPriceNew(symbol string, tradeType int64) (decimal.Decimal, error) {
	cacheKey := SymbolCache(flags.Wh, symbol, flags.TradeTypePrice)
	priceNew, err := memory.GetForexImmediateCache(cacheKey)
	if err != nil {
		return decimal.Decimal{}, err
	}

	priceList, err := decimal.NewFromString(string(priceNew))
	if err != nil {
		applogger.Error("%v ForexTransactionCalculationPosition.Get:%v", common.ErrForex, err)
		return decimal.Decimal{}, err
	}

	applogger.Info("ForexCode:%v,topIc:%v,ForexPrice:%v", symbol, cacheKey, priceList)

	return priceList, nil
}