1、
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go-admin/common/const/rediskey"
|
||||
"go-admin/common/global"
|
||||
@ -284,7 +285,15 @@ func (e LinePreOrder) AddPreOrder(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userId := user.GetUserId(c)
|
||||
|
||||
if userId <= 0 {
|
||||
e.Error(500, errors.New("用户不存在"), "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
req.SetCreateBy(userId)
|
||||
errs := make([]error, 0)
|
||||
errStr := make([]string, 0)
|
||||
var tickerSymbol string
|
||||
@ -358,6 +367,13 @@ func (e LinePreOrder) BatchAddOrder(c *gin.Context) {
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
errs := make([]error, 0)
|
||||
errStr := make([]string, 0)
|
||||
userId := user.GetUserId(c)
|
||||
|
||||
if userId <= 0 {
|
||||
e.Error(500, nil, "用户不存在")
|
||||
return
|
||||
}
|
||||
req.SetCreateBy(userId)
|
||||
s.AddBatchPreOrder(&req, p, &errs)
|
||||
if len(errs) > 0 {
|
||||
//e.Logger.Error(err)
|
||||
@ -392,7 +408,14 @@ func (e LinePreOrder) QuickAddPreOrder(c *gin.Context) {
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
errs := make([]error, 0)
|
||||
errStr := make([]string, 0)
|
||||
err = s.QuickAddPreOrder(&req, p, &errs)
|
||||
userId := user.GetUserId(c)
|
||||
|
||||
if userId <= 0 {
|
||||
e.Error(500, nil, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
err = s.QuickAddPreOrder(&req, p, userId, &errs)
|
||||
if len(errs) > 0 {
|
||||
//e.Logger.Error(err)
|
||||
for _, err2 := range errs {
|
||||
|
||||
@ -26,7 +26,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -364,6 +363,11 @@ func (e LineUserApi) Info(c *gin.Context) {
|
||||
binanceAccount := service.BinanceAccount{Service: s.Service}
|
||||
|
||||
//获取用户资金账户资产
|
||||
totalAsset, err := binanceAccount.GetTotalAsset(userId)
|
||||
|
||||
if err != nil {
|
||||
e.Logger.Errorf("获取用户资金账户资产失败:%v", err)
|
||||
}
|
||||
resp, err := binanceAccount.GetFundingAsset(userId)
|
||||
if err != nil {
|
||||
e.Logger.Error(500, err, err.Error())
|
||||
@ -375,28 +379,26 @@ func (e LineUserApi) Info(c *gin.Context) {
|
||||
if val != "" {
|
||||
sonic.Unmarshal([]byte(val), &tickerData)
|
||||
}
|
||||
var usdtBalance decimal.Decimal
|
||||
// var usdtBalance decimal.Decimal
|
||||
for i, asset := range resp {
|
||||
symbol := asset.Asset + "USDT"
|
||||
for _, datum := range tickerData {
|
||||
if datum.Symbol == symbol {
|
||||
mul := utility.StringToDecimal(datum.Price).Mul(utility.StringToDecimal(asset.Free))
|
||||
usdtBalance = usdtBalance.Add(mul)
|
||||
// mul := utility.StringToDecimal(datum.Price).Mul(utility.StringToDecimal(asset.Free))
|
||||
// usdtBalance = usdtBalance.Add(mul)
|
||||
resp[i].UsdtValuation = datum.Price
|
||||
}
|
||||
}
|
||||
//if asset.Asset == "USDT" {
|
||||
// usdt = asset.Free
|
||||
//}
|
||||
}
|
||||
|
||||
// 邀请人数
|
||||
//var inviteNum int64
|
||||
var inviteNum int64
|
||||
var userinfo models.LineUser
|
||||
e.Orm.Model(&models.LineUser{}).Where("id = ?", userId).Find(&userinfo)
|
||||
e.Orm.Model(&models.LineUser{}).Where("pid =? or top_referrer_id =?", userId, userId).Count(&inviteNum)
|
||||
|
||||
var apiUserinfo models.LineApiUser
|
||||
e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Find(&apiUserinfo)
|
||||
e.Orm.Model(&models.LineApiUser{}).Where("user_id = ? ", userId).Find(&apiUserinfo)
|
||||
var isAuth bool
|
||||
if apiUserinfo.ApiKey != "" && apiUserinfo.ApiSecret != "" {
|
||||
isAuth = true
|
||||
@ -418,7 +420,7 @@ func (e LineUserApi) Info(c *gin.Context) {
|
||||
"avatar": userinfo.Avatar,
|
||||
"user_id": userinfo.Id,
|
||||
"user_name": userinfo.Nickname,
|
||||
"invite_num": userinfo.RecommendNum,
|
||||
"invite_num": inviteNum,
|
||||
"open_status": apiUserinfo.OpenStatus,
|
||||
"is_auth": isAuth,
|
||||
"invite_url": fmt.Sprintf("%s/invice_url?invite_code=%s", ext.ExtConfig.Domain, userinfo.InviteCode),
|
||||
@ -428,7 +430,7 @@ func (e LineUserApi) Info(c *gin.Context) {
|
||||
"api_secret": inttostring.EncryptString(apiUserinfo.ApiSecret, 4, 4),
|
||||
}
|
||||
returnMap := map[string]interface{}{
|
||||
"u_balance": usdtBalance.Truncate(2),
|
||||
"u_balance": totalAsset.Truncate(2),
|
||||
"margin": userinfo.Money,
|
||||
"userinfo": user,
|
||||
"funding_asset": fundingAsset,
|
||||
|
||||
@ -9,3 +9,9 @@ type FundingAsset struct {
|
||||
BtcValuation string `json:"btcValuation"`
|
||||
UsdtValuation string `json:"usdt_valuation"`
|
||||
}
|
||||
|
||||
type BinanceWalletBalance struct {
|
||||
Active bool `json:"active"`
|
||||
Balance string `json:"balance"`
|
||||
WalletName string `json:"walletName"`
|
||||
}
|
||||
|
||||
@ -3,12 +3,19 @@ package service
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/common/const/rediskey"
|
||||
"go-admin/common/global"
|
||||
"go-admin/common/helper"
|
||||
ext "go-admin/config"
|
||||
"go-admin/pkg/utility"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
const ProxyType = "socks5"
|
||||
@ -62,3 +69,90 @@ func (e *BinanceAccount) GetFundingAsset(userId int) (resp []models.FundingAsset
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
/*
|
||||
获取钱包余额
|
||||
|
||||
- @param userId 用户id
|
||||
- @param quoteAsset 币种 USDT, ETH, USDC, BNB等。 默认 BTC
|
||||
- @return []models.FundingAsset 钱包余额
|
||||
*/
|
||||
func (e *BinanceAccount) GetWalletBalance(userId int, quoteAsset string) (resp []models.BinanceWalletBalance, err error) {
|
||||
if quoteAsset == "" {
|
||||
quoteAsset = "BTC"
|
||||
}
|
||||
|
||||
var proxyUrl, proxyType string
|
||||
var apiUser models.LineApiUser
|
||||
url := "/sapi/v1/asset/wallet/balance"
|
||||
err = e.Orm.Where(&models.LineApiUser{}).Where("user_id = ?", userId).Find(&apiUser).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("Service LineApiUser error:%s \r\n", err)
|
||||
return []models.BinanceWalletBalance{}, err
|
||||
}
|
||||
if apiUser.Id <= 0 {
|
||||
e.Log.Errorf("Service LineApiUser error:%s \r\n", "用户未找到")
|
||||
return []models.BinanceWalletBalance{}, err
|
||||
}
|
||||
|
||||
if ext.ExtConfig.ProxyUrl != "" {
|
||||
proxyUrl = "127.0.0.1:7890"
|
||||
proxyType = "http"
|
||||
}
|
||||
|
||||
if apiUser.IpAddress != "" && apiUser.UserPass != "" {
|
||||
proxyUrl = fmt.Sprintf("%s:%s", apiUser.IpAddress, apiUser.UserPass)
|
||||
proxyType = ProxyType
|
||||
}
|
||||
clinet, err := helper.NewBinanceClient(apiUser.ApiKey, apiUser.ApiSecret, proxyType, proxyUrl)
|
||||
if err != nil {
|
||||
e.Log.Errorf("Service NewBinanceClient error:%s \r\n", err)
|
||||
return []models.BinanceWalletBalance{}, err
|
||||
}
|
||||
req := map[string]string{
|
||||
"quoteAsset": quoteAsset,
|
||||
"recvWindow": "10000",
|
||||
}
|
||||
|
||||
httpResp, statusCode, err := clinet.SendSpotAuth(url, "GET", req)
|
||||
if err != nil {
|
||||
e.Log.Errorf("Service SendSpotAuth error:%s \r\n", err)
|
||||
return []models.BinanceWalletBalance{}, err
|
||||
}
|
||||
|
||||
if statusCode != http.StatusOK {
|
||||
e.Log.Errorf("Service 请求失败 error:%s \r\n", err)
|
||||
return []models.BinanceWalletBalance{}, errors.New("请求失败")
|
||||
}
|
||||
sonic.Unmarshal(httpResp, &resp)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
/*
|
||||
获取用户总资产
|
||||
*/
|
||||
func (e *BinanceAccount) GetTotalAsset(userId int) (decimal.Decimal, error) {
|
||||
key := fmt.Sprintf(rediskey.User_Total_Asset, global.EXCHANGE_BINANCE, userId)
|
||||
totalStr, _ := helper.DefaultRedis.GetString(key)
|
||||
result := decimal.Zero
|
||||
if totalStr != "" {
|
||||
result = utility.StrToDecimal(totalStr)
|
||||
} else {
|
||||
assets, err := e.GetWalletBalance(userId, "USDT")
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("GetTotalAsset error:%s", err)
|
||||
|
||||
return decimal.Decimal{}, err
|
||||
}
|
||||
|
||||
for _, asset := range assets {
|
||||
result = result.Add(utility.StrToDecimal(asset.Balance))
|
||||
}
|
||||
|
||||
helper.DefaultRedis.SetStringExpire(key, result.String(), 30*time.Second)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@ -209,6 +209,7 @@ type LineAddPreOrderReq struct {
|
||||
ReduceReTakeProfitRatio decimal.Decimal `json:"re_take_profit_ratio" comment:"减仓后亏损回本止盈百分比"`
|
||||
|
||||
Ext []LineAddPreOrderExtReq `json:"ext" ` //拓展字段
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
type LinePreOrderAddPositionReq struct {
|
||||
@ -389,6 +390,7 @@ type LineBatchAddPreOrderReq struct {
|
||||
ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比
|
||||
ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比
|
||||
Ext []LineAddPreOrderExtReq `json:"ext"` //拓展字段
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
func (req LineBatchAddPreOrderReq) CheckParams() error {
|
||||
|
||||
@ -18,10 +18,12 @@ type LineSymbolGetPageReq struct {
|
||||
}
|
||||
|
||||
type LineSymbolExportResp struct {
|
||||
Symbol string `json:"symbol" excel:"交易对"`
|
||||
Coin string `json:"coin" excel:"基础货币"`
|
||||
Currency string `json:"currency" excel:"计价货币"`
|
||||
SymbolType string `json:"symbolType" excel:"交易对类型"`
|
||||
ExchangeType string `json:"exchangeType" excel:"交易所"`
|
||||
Symbol string `json:"symbol" excel:"交易对"`
|
||||
Coin string `json:"coin" excel:"基础货币"`
|
||||
Currency string `json:"currency" excel:"计价货币"`
|
||||
SymbolType string `json:"symbolType" excel:"交易对类型"`
|
||||
LastPrice string `json:"lastPrice" excel:"最新价"`
|
||||
}
|
||||
|
||||
type LineSymbolOrder struct {
|
||||
|
||||
@ -278,14 +278,15 @@ func (receiver FrontedLoginReq) CheckParams() int {
|
||||
}
|
||||
|
||||
type AddApiKeyReq struct {
|
||||
ApiName string `json:"api_name"`
|
||||
ApiKey string `json:"api_key"`
|
||||
ApiSecret string `json:"api_secret"`
|
||||
ApiIp string `json:"api_ip"`
|
||||
ExchangeType string `json:"exchange_type"`
|
||||
ApiName string `json:"api_name"`
|
||||
ApiKey string `json:"api_key"`
|
||||
ApiSecret string `json:"api_secret"`
|
||||
ApiIp string `json:"api_ip"`
|
||||
}
|
||||
|
||||
func (a AddApiKeyReq) CheckParams() int {
|
||||
if a.ApiKey == "" || a.ApiSecret == "" {
|
||||
if a.ExchangeType == "" || a.ApiKey == "" || a.ApiSecret == "" {
|
||||
return statuscode.ParamErr
|
||||
}
|
||||
return statuscode.OK
|
||||
|
||||
@ -29,7 +29,7 @@ func (e *LineDirection) GetPage(c *dto.LineDirectionGetPageReq, p *actions.DataP
|
||||
Scopes(
|
||||
cDto.MakeCondition(c.GetNeedSearch()),
|
||||
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||
actions.Permission(data.TableName(), p),
|
||||
// actions.Permission(data.TableName(), p),
|
||||
).
|
||||
Find(list).Limit(-1).Offset(-1).
|
||||
Count(count).Error
|
||||
@ -45,9 +45,9 @@ func (e *LineDirection) Get(d *dto.LineDirectionGetReq, p *actions.DataPermissio
|
||||
var data models.LineDirection
|
||||
|
||||
err := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).
|
||||
// Scopes(
|
||||
// actions.Permission(data.TableName(), p),
|
||||
// ).
|
||||
First(model, d.GetId()).Error
|
||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = errors.New("查看对象不存在或无权查看")
|
||||
|
||||
@ -391,6 +391,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
|
||||
Type: 1,
|
||||
Switch: "0",
|
||||
}
|
||||
saveTemplateParams.CreateBy = req.CreateBy
|
||||
e.Orm.Model(&models.LineOrderTemplateLogs{}).Create(&saveTemplateParams)
|
||||
}
|
||||
if req.SaveTemplate == "2" {
|
||||
@ -426,6 +427,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
|
||||
continue
|
||||
}
|
||||
|
||||
AddOrder.CreateBy = req.CreateBy
|
||||
AddOrder.ExchangeType = req.ExchangeType
|
||||
AddOrder.OrderCategory = 1
|
||||
AddOrder.SignPriceType = "new"
|
||||
@ -598,6 +600,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
|
||||
ext.TotalBefore = mainParam.RemainingQuantity //初始数量
|
||||
ext.TotalAfter = calculateResp.RemainingQuantity //计算后数量
|
||||
ext.ReTakeRatio = calculateResp.Ratio
|
||||
ext.CreateBy = req.CreateBy
|
||||
mainParam.LossBeginPercent = addPosition.PriceRatio
|
||||
mainParam.RemainingQuantity = calculateResp.RemainingQuantity
|
||||
mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU
|
||||
@ -637,18 +640,29 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
|
||||
|
||||
//是否有止盈止损订单
|
||||
if req.Profit != "" {
|
||||
if strings.ToUpper(req.Site) == "BUY" {
|
||||
profitOrder.Site = "SELL"
|
||||
profitOrder.Price = decimal.NewFromFloat(utility.StringToFloat64(AddOrder.Price) * (1 + utility.StringToFloat64(req.Profit)/100)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
if req.PricePattern == "mixture" {
|
||||
mixturePrice := utility.StrToDecimal(req.Profit)
|
||||
|
||||
if mixturePrice.Cmp(decimal.Zero) <= 0 {
|
||||
return fmt.Errorf("止盈价不能小于等于0")
|
||||
}
|
||||
|
||||
profitOrder.Price = mixturePrice.Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
profitOrder.Rate = "0"
|
||||
} else {
|
||||
profitOrder.Site = "BUY"
|
||||
profitOrder.Price = decimal.NewFromFloat(utility.StringToFloat64(AddOrder.Price) * (1 - utility.StringToFloat64(req.Profit)/100)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
if strings.ToUpper(req.Site) == "BUY" {
|
||||
profitOrder.Site = "SELL"
|
||||
profitOrder.Price = decimal.NewFromFloat(utility.StringToFloat64(AddOrder.Price) * (1 + utility.StringToFloat64(req.Profit)/100)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
} else {
|
||||
profitOrder.Site = "BUY"
|
||||
profitOrder.Price = decimal.NewFromFloat(utility.StringToFloat64(AddOrder.Price) * (1 - utility.StringToFloat64(req.Profit)/100)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
}
|
||||
profitOrder.Rate = req.Profit
|
||||
}
|
||||
profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
|
||||
profitOrder.Pid = AddOrder.Id
|
||||
profitOrder.OrderType = 1
|
||||
profitOrder.Status = 0
|
||||
profitOrder.Rate = req.Profit
|
||||
profitOrder.MainId = AddOrder.Id
|
||||
|
||||
if req.ProfitNumRatio.Cmp(decimal.Zero) > 0 {
|
||||
@ -673,12 +687,23 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
|
||||
}
|
||||
|
||||
if req.ReducePriceRatio.Cmp(decimal.Zero) > 0 {
|
||||
if strings.ToUpper(req.Site) == "BUY" {
|
||||
stopOrder.Site = "SELL"
|
||||
stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
if req.PricePattern == "mixture" {
|
||||
if req.ReducePriceRatio.Cmp(decimal.Zero) <= 0 {
|
||||
return errors.New("检查价格不能小于等于0")
|
||||
}
|
||||
|
||||
stopOrder.Price = req.ReducePriceRatio.Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
stopOrder.Rate = "0"
|
||||
} else {
|
||||
stopOrder.Site = "BUY"
|
||||
stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
if strings.ToUpper(req.Site) == "BUY" {
|
||||
stopOrder.Site = "SELL"
|
||||
stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
} else {
|
||||
stopOrder.Site = "BUY"
|
||||
stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
}
|
||||
|
||||
stopOrder.Rate = req.ReducePriceRatio.String()
|
||||
}
|
||||
stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
|
||||
stopOrder.Pid = AddOrder.Id
|
||||
@ -686,7 +711,6 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
|
||||
stopOrder.OrderType = 4
|
||||
stopOrder.Status = 0
|
||||
stopOrder.BuyPrice = "0"
|
||||
stopOrder.Rate = req.ReducePriceRatio.String()
|
||||
stopNum := utility.StrToDecimal(AddOrder.Num).Mul(req.ReduceNumRatio.Div(decimal.NewFromInt(100)).Truncate(4))
|
||||
stopOrder.Num = stopNum.Truncate(int32(tradeSet.AmountDigit)).String()
|
||||
stopOrder.ExpireTime = time.Now().AddDate(10, 0, 0)
|
||||
@ -1068,6 +1092,8 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p
|
||||
Type: 2,
|
||||
Switch: "0",
|
||||
}
|
||||
|
||||
saveTemplateParams.CreateBy = p.UserId
|
||||
e.Orm.Model(&models.LineOrderTemplateLogs{}).Create(&saveTemplateParams)
|
||||
}
|
||||
|
||||
@ -1149,6 +1175,7 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p
|
||||
req.ReduceNumRatio = batchReq.ReduceNumRatio
|
||||
req.ReduceStopLossRatio = batchReq.ReduceStopLossRatio
|
||||
req.ReduceTakeProfitRatio = batchReq.ReduceTakeProfitRatio
|
||||
req.CreateBy = batchReq.CreateBy
|
||||
|
||||
e.AddPreOrder(&req, p, errs, tickerSymbol)
|
||||
}
|
||||
@ -1162,7 +1189,7 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p
|
||||
}
|
||||
|
||||
// QuickAddPreOrder 模板快速下单
|
||||
func (e *LinePreOrder) QuickAddPreOrder(quickReq *dto.QuickAddPreOrderReq, p *actions.DataPermission, errs *[]error) error {
|
||||
func (e *LinePreOrder) QuickAddPreOrder(quickReq *dto.QuickAddPreOrderReq, p *actions.DataPermission, userId int, errs *[]error) error {
|
||||
templateLogs := make([]models.LineOrderTemplateLogs, 0)
|
||||
e.Orm.Model(&models.LineOrderTemplateLogs{}).Where("id in ?", strings.Split(quickReq.Ids, ",")).Find(&templateLogs)
|
||||
for _, log := range templateLogs {
|
||||
@ -1188,6 +1215,13 @@ func (e *LinePreOrder) QuickAddPreOrder(quickReq *dto.QuickAddPreOrderReq, p *ac
|
||||
if log.Type == 2 {
|
||||
var batchAddPreOrder dto.LineBatchAddPreOrderReq
|
||||
sonic.Unmarshal([]byte(log.Params), &batchAddPreOrder)
|
||||
|
||||
if userId > 0 {
|
||||
batchAddPreOrder.CreateBy = userId
|
||||
} else {
|
||||
batchAddPreOrder.CreateBy = log.CreateBy
|
||||
}
|
||||
|
||||
e.AddBatchPreOrder(&batchAddPreOrder, p, errs)
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ import (
|
||||
"go-admin/common/global"
|
||||
"go-admin/common/helper"
|
||||
commonModels "go-admin/models"
|
||||
models2 "go-admin/models"
|
||||
"go-admin/pkg/utility"
|
||||
"go-admin/services/binanceservice"
|
||||
)
|
||||
@ -37,7 +38,7 @@ func (e *LineSymbol) GetPage(c *dto.LineSymbolGetPageReq, p *actions.DataPermiss
|
||||
Scopes(
|
||||
cDto.MakeCondition(c.GetNeedSearch()),
|
||||
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||
actions.Permission(data.TableName(), p),
|
||||
// actions.Permission(data.TableName(), p),
|
||||
).
|
||||
Find(list).Limit(-1).Offset(-1).
|
||||
Count(count).Error
|
||||
@ -126,7 +127,7 @@ func (e *LineSymbol) ExportExcel(c *gin.Context, p *actions.DataPermission, req
|
||||
err := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
cDto.MakeCondition(req.GetNeedSearch()),
|
||||
actions.Permission(data.TableName(), p),
|
||||
// actions.Permission(data.TableName(), p),
|
||||
).
|
||||
Find(&list).Error
|
||||
|
||||
@ -134,17 +135,64 @@ func (e *LineSymbol) ExportExcel(c *gin.Context, p *actions.DataPermission, req
|
||||
return err
|
||||
}
|
||||
|
||||
exchanges := []string{}
|
||||
exchangeFutMap := make(map[string]models2.TradeSet)
|
||||
exchangeSpotMap := make(map[string]models2.TradeSet)
|
||||
|
||||
for _, v := range list {
|
||||
if !utility.ContainsStr(exchanges, v.ExchangeType) {
|
||||
key := fmt.Sprintf(global.TICKER_FUTURES, v.ExchangeType, "*")
|
||||
key2 := fmt.Sprintf(global.TICKER_SPOT, v.ExchangeType, "*")
|
||||
futs, _ := helper.DefaultRedis.GetAllKeysAndValues(key)
|
||||
spots, _ := helper.DefaultRedis.GetAllKeysAndValues(key2)
|
||||
trade := models2.TradeSet{}
|
||||
|
||||
for _, v2 := range futs {
|
||||
if v2 != "" {
|
||||
sonic.Unmarshal([]byte(v2), &trade)
|
||||
|
||||
if trade.LastPrice != "" {
|
||||
exchangeFutMap[v.ExchangeType+"_"+trade.Coin+trade.Currency] = trade
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, v2 := range spots {
|
||||
if v2 != "" {
|
||||
sonic.Unmarshal([]byte(v2), &trade)
|
||||
|
||||
if trade.LastPrice != "" {
|
||||
exchangeSpotMap[v.ExchangeType+"_"+trade.Coin+trade.Currency] = trade
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exchanges = append(exchanges, v.ExchangeType)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
item := dto.LineSymbolExportResp{
|
||||
Symbol: v.Symbol,
|
||||
Coin: v.BaseAsset,
|
||||
Currency: v.QuoteAsset,
|
||||
ExchangeType: v.ExchangeType,
|
||||
Symbol: v.Symbol,
|
||||
Coin: v.BaseAsset,
|
||||
Currency: v.QuoteAsset,
|
||||
}
|
||||
|
||||
if v.Type == "1" {
|
||||
item.SymbolType = "现货"
|
||||
if v, ok := exchangeSpotMap[v.ExchangeType+"_"+v.Symbol]; ok {
|
||||
if v.LastPrice != "" {
|
||||
item.LastPrice = v.LastPrice
|
||||
}
|
||||
}
|
||||
} else {
|
||||
item.SymbolType = "合约"
|
||||
if v, ok := exchangeFutMap[v.ExchangeType+"_"+v.Symbol]; ok {
|
||||
if v.LastPrice != "" {
|
||||
item.LastPrice = v.LastPrice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
datas = append(datas, item)
|
||||
|
||||
@ -51,7 +51,7 @@ func (e *LineUser) GetPage(c *dto.LineUserGetPageReq, p *actions.DataPermission,
|
||||
Scopes(
|
||||
cDto.MakeCondition(c.GetNeedSearch()),
|
||||
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||
actions.Permission(data.TableName(), p),
|
||||
// actions.Permission(data.TableName(), p),
|
||||
).
|
||||
Find(list).Limit(-1).Offset(-1).
|
||||
Count(count).Error
|
||||
@ -225,15 +225,16 @@ func (e *LineUser) AddApiKey(userId int, req *dto.AddApiKeyReq) int {
|
||||
}
|
||||
|
||||
err = e.Orm.Model(&models.LineApiUser{}).Create(&models.LineApiUser{
|
||||
UserId: int64(userId),
|
||||
ApiName: req.ApiName,
|
||||
ApiKey: req.ApiKey,
|
||||
ApiSecret: req.ApiSecret,
|
||||
Affiliation: 3,
|
||||
AdminShow: 0,
|
||||
Site: "3",
|
||||
Subordinate: "0",
|
||||
OpenStatus: 0,
|
||||
ExchangeType: req.ExchangeType,
|
||||
UserId: int64(userId),
|
||||
ApiName: req.ApiName,
|
||||
ApiKey: req.ApiKey,
|
||||
ApiSecret: req.ApiSecret,
|
||||
Affiliation: 3,
|
||||
AdminShow: 0,
|
||||
Site: "3",
|
||||
Subordinate: "0",
|
||||
OpenStatus: 0,
|
||||
}).Error
|
||||
|
||||
if err != nil {
|
||||
@ -743,6 +744,8 @@ func (e *LineUser) OpenStatus(req *dto.OpenStatusReq, userId int) int {
|
||||
propperty := dto.LineUserPropertyResp{}
|
||||
e.GetProperty(userId, &propperty)
|
||||
|
||||
logger.Infof("合约可用资产:%v", propperty.FuturesFreeAmount)
|
||||
logger.Infof("现货可用资产:%v", propperty.SpotFreeAmount)
|
||||
//可用资产不足
|
||||
if propperty.FuturesFreeAmount.Cmp(userSet.MinOrderAmount) < 0 && propperty.SpotFreeAmount.Cmp(userSet.MinOrderAmount) < 0 {
|
||||
return statuscode.PropertyInsufficient
|
||||
|
||||
@ -142,7 +142,7 @@ func (t AutoPlaceOrder) Exec(arg interface{}) error {
|
||||
preOrderService.Orm = db
|
||||
errs := make([]error, 0)
|
||||
errStr := make([]string, 0)
|
||||
err := preOrderService.QuickAddPreOrder(&req, nil, &errs)
|
||||
err := preOrderService.QuickAddPreOrder(&req, nil, 0, &errs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
6
common/const/rediskey/control.go
Normal file
6
common/const/rediskey/control.go
Normal file
@ -0,0 +1,6 @@
|
||||
package rediskey
|
||||
|
||||
const (
|
||||
//用户总资产 {exchange_type,user_id}
|
||||
User_Total_Asset = "u_total_asset:%s:%v"
|
||||
)
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
@ -20,7 +21,9 @@ import (
|
||||
*/
|
||||
func CreateHtppProxy(proxyType, proxyAddr string, client *http.Client) error {
|
||||
// Set up proxy based on type (HTTP, HTTPS, SOCKS5)
|
||||
transport := &http.Transport{}
|
||||
transport := &http.Transport{
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
}
|
||||
if proxyAddr != "" {
|
||||
if !strings.HasPrefix(proxyAddr, "http://") && !strings.HasPrefix(proxyAddr, "https://") && !strings.HasPrefix(proxyAddr, "socks5://") {
|
||||
proxyAddr = proxyType + "://" + proxyAddr
|
||||
|
||||
@ -1,18 +1,13 @@
|
||||
package authservice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go-admin/common/const/rediskey"
|
||||
"go-admin/common/helper"
|
||||
statuscode "go-admin/common/status_code"
|
||||
ext "go-admin/config"
|
||||
"go-admin/pkg/cryptohelper/inttostring"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@ -496,60 +491,8 @@ func SendGoToneSms(phone, area string, smsType int) int {
|
||||
}
|
||||
//ext.ExtConfig.GoToneSmsConfig
|
||||
// SmsRequest 用于构建发送短信请求的结构体
|
||||
type SmsRequest struct {
|
||||
Recipient string `json:"recipient"` // 收件人电话号码
|
||||
Message string `json:"message"` // 短信内容
|
||||
SenderId string `json:"sender_id"` // 发送者名称
|
||||
Type string `json:"type"`
|
||||
}
|
||||
// 创建请求数据
|
||||
smsRequest := SmsRequest{
|
||||
Recipient: "+" + area + phone,
|
||||
SenderId: ext.ExtConfig.GoToneSmsConfig.SenderId,
|
||||
Message: fmt.Sprintf("欢迎使用 GoTone SMS,高速稳定地发送短信至中国大陆及全球用户,体验验证码:%s。如非本人操作请忽略此信息", smsString),
|
||||
Type: "plain",
|
||||
}
|
||||
// 将请求数据编码为 JSON
|
||||
requestBody, err := sonic.Marshal(smsRequest)
|
||||
if err != nil {
|
||||
log.Error("GoToneSms requestBody Error:", err)
|
||||
return statuscode.ServerError
|
||||
}
|
||||
// 创建 HTTP 请求
|
||||
req, err := http.NewRequest("POST", ext.ExtConfig.GoToneSmsConfig.APIEndpoint, bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
log.Error("GoToneSms http.NewRequest Error:", err)
|
||||
return statuscode.ServerError
|
||||
}
|
||||
// 设置请求头
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+ext.ExtConfig.GoToneSmsConfig.Authorization) // 使用 API 密钥进行身份验证
|
||||
// todo 短信cangchu
|
||||
|
||||
// 创建 HTTP 客户端并发送请求
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second, // 设置请求超时时间
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
fmt.Println("resp:", resp)
|
||||
if err != nil {
|
||||
log.Error("GoToneSms do NewRequest Error:", err)
|
||||
return statuscode.CaptchaFailInSend
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查响应状态
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Error("GoToneSms do NewRequest Error:", err)
|
||||
return statuscode.CaptchaFailInSend
|
||||
}
|
||||
// 读取响应体
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Error("读取响应体失败:", err)
|
||||
fmt.Printf("响应体: %s", string(body))
|
||||
return statuscode.CaptchaFailInSend
|
||||
//return fmt.Errorf("读取响应体失败: %v", err)
|
||||
}
|
||||
// 打印响应内容(调试用)
|
||||
//记录短信发送操作
|
||||
helper.DefaultRedis.SetStringExpire(registerKey, "1", time.Second*60)
|
||||
|
||||
@ -16,11 +16,11 @@ type Extend struct {
|
||||
EmailConfig EmailConfig `mapstructure:"emailConfig"`
|
||||
BinanceSet BinanceConfig `mapstructure:"binanceSet"` //binance配置
|
||||
Domain string //网站域名
|
||||
GoToneSmsConfig GoToneSmsConfig `mapstructure:"GoToneSmsConfig"`
|
||||
UDunConfig UDunConfig `mapstructure:"UDunConfig"`
|
||||
ProxyUrl string //代理地址
|
||||
CoinGate CoinGateConfig `mapstructure:"coingate"` //coingate钱包
|
||||
|
||||
GoToneSmsConfig GoToneSmsConfig `mapstructure:"GoToneSmsConfig"`
|
||||
InnoPaas InnoPaasConfig `mapstructure:"innoPaas"` //创蓝短信
|
||||
}
|
||||
|
||||
type CoinGateConfig struct {
|
||||
@ -70,6 +70,12 @@ type GoToneSmsConfig struct {
|
||||
Authorization string `json:"authorization"`
|
||||
}
|
||||
|
||||
type InnoPaasConfig struct {
|
||||
Url string `json:"url"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type UDunConfig struct {
|
||||
UDunUrl string `json:"UDunUrl"`
|
||||
UDunMerchantID string `json:"UDunMerchantID"`
|
||||
|
||||
@ -11,7 +11,7 @@ settings:
|
||||
readtimeout: 1
|
||||
writertimeout: 2
|
||||
# 数据权限功能开关
|
||||
enabledp: false
|
||||
enabledp: true
|
||||
logger:
|
||||
# 日志存放路径
|
||||
path: temp/logs
|
||||
@ -82,6 +82,10 @@ settings:
|
||||
sender_id: "GoTone SMS"
|
||||
api_endpoint: "https://gosms.one/api/v3/sms/send"
|
||||
authorization: "CVZgh3iIAQpJuvaakQmxOo9q2uOb7Veqs7ls5KIX263d87ee"
|
||||
InnoPaas:
|
||||
url: "http://intapi.sgap.253.com"
|
||||
apiKey: "OI1706483"
|
||||
password: "N4R84hhVvP6505"
|
||||
|
||||
#UDun 配置
|
||||
UDunConfig:
|
||||
|
||||
@ -739,7 +739,7 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd
|
||||
for _, order := range orders {
|
||||
order.Num = num.String()
|
||||
|
||||
if fist && order.OrderCategory == 1 && order.OrderType == 1 && orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 {
|
||||
if fist && (order.OrderCategory == 3 || order.OrderCategory == 1) && order.OrderType == 1 && orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 {
|
||||
//主单第一次且止盈数量不是100% 止盈数量
|
||||
order.Num = num.Mul(orderExt.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String()
|
||||
}
|
||||
@ -761,7 +761,7 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd
|
||||
order.Rate = percentag.String()
|
||||
percentag = percentag.Div(decimal.NewFromInt(100))
|
||||
order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
} else if orderExt.Id > 0 {
|
||||
} else if !fist && orderExt.TpTpPriceRatio.Cmp(decimal.Zero) > 0 {
|
||||
percentag := orderExt.TpTpPriceRatio
|
||||
order.Rate = percentag.String()
|
||||
order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
|
||||
|
||||
76
services/smsservice/gotone_service.go
Normal file
76
services/smsservice/gotone_service.go
Normal file
@ -0,0 +1,76 @@
|
||||
package smsservice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
statuscode "go-admin/common/status_code"
|
||||
"go-admin/config"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
)
|
||||
|
||||
type GotoneService struct {
|
||||
}
|
||||
|
||||
func (s *GotoneService) SendSMS(area, phonenumber string, content string) int {
|
||||
type SmsRequest struct {
|
||||
Recipient string `json:"recipient"` // 收件人电话号码
|
||||
Message string `json:"message"` // 短信内容
|
||||
SenderId string `json:"sender_id"` // 发送者名称
|
||||
Type string `json:"type"`
|
||||
}
|
||||
// 创建请求数据
|
||||
smsRequest := SmsRequest{
|
||||
Recipient: "+" + area + phonenumber,
|
||||
SenderId: config.ExtConfig.GoToneSmsConfig.SenderId,
|
||||
Message: fmt.Sprintf("欢迎使用 GoTone SMS,高速稳定地发送短信至中国大陆及全球用户,体验验证码:%s。如非本人操作请忽略此信息", content),
|
||||
Type: "plain",
|
||||
}
|
||||
// 将请求数据编码为 JSON
|
||||
requestBody, err := sonic.Marshal(smsRequest)
|
||||
if err != nil {
|
||||
logger.Error("GoToneSms requestBody Error:", err)
|
||||
return statuscode.ServerError
|
||||
}
|
||||
// 创建 HTTP 请求
|
||||
req, err := http.NewRequest("POST", config.ExtConfig.GoToneSmsConfig.APIEndpoint, bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
logger.Error("GoToneSms http.NewRequest Error:", err)
|
||||
return statuscode.ServerError
|
||||
}
|
||||
// 设置请求头
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+config.ExtConfig.GoToneSmsConfig.Authorization) // 使用 API 密钥进行身份验证
|
||||
|
||||
// 创建 HTTP 客户端并发送请求
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second, // 设置请求超时时间
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
fmt.Println("resp:", resp)
|
||||
if err != nil {
|
||||
logger.Error("GoToneSms do NewRequest Error:", err)
|
||||
return statuscode.CaptchaFailInSend
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查响应状态
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logger.Error("GoToneSms do NewRequest Error:", err)
|
||||
return statuscode.CaptchaFailInSend
|
||||
}
|
||||
// 读取响应体
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logger.Error("读取响应体失败:", err)
|
||||
fmt.Printf("响应体: %s", string(body))
|
||||
return statuscode.CaptchaFailInSend
|
||||
//return fmt.Errorf("读取响应体失败: %v", err)
|
||||
}
|
||||
|
||||
return statuscode.OK
|
||||
}
|
||||
44
services/smsservice/innopass_service.go
Normal file
44
services/smsservice/innopass_service.go
Normal file
@ -0,0 +1,44 @@
|
||||
package smsservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
statuscode "go-admin/common/status_code"
|
||||
"go-admin/config"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
)
|
||||
|
||||
type InnopaasService struct {
|
||||
}
|
||||
|
||||
func (i *InnopaasService) SendSMS(area, phoneNumber string, message string) int {
|
||||
// Implement the logic to send SMS using Innopass API
|
||||
params := map[string]string{
|
||||
"account": config.ExtConfig.InnoPaas.ApiKey,
|
||||
"password": config.ExtConfig.InnoPaas.Password,
|
||||
"mobile": area + phoneNumber,
|
||||
"msg": message,
|
||||
}
|
||||
payload, _ := sonic.MarshalString(params) // strings.NewReader("{\"account\":\"I7145744\",\"password\":\"password\",\"mobile\":\"0012012074149,0012012074142\",\"msg\":\"Your verification code is 8888\"}")
|
||||
|
||||
req, _ := http.NewRequest("POST", config.ExtConfig.InnoPaas.Url, strings.NewReader(payload))
|
||||
|
||||
req.Header.Add("accept", "application/json")
|
||||
req.Header.Add("content-type", "application/json")
|
||||
|
||||
res, _ := http.DefaultClient.Do(req)
|
||||
|
||||
defer res.Body.Close()
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
logger.Error("读取响应体失败:", err)
|
||||
fmt.Printf("响应体: %s", string(body))
|
||||
return statuscode.CaptchaFailInSend
|
||||
//return fmt.Errorf("读取响应体失败: %v", err)
|
||||
}
|
||||
return statuscode.OK
|
||||
}
|
||||
13
services/smsservice/sms_service.go
Normal file
13
services/smsservice/sms_service.go
Normal file
@ -0,0 +1,13 @@
|
||||
package smsservice
|
||||
|
||||
type SMSService interface {
|
||||
// SendSMS 发送短信
|
||||
// area 区号
|
||||
// phoneNumber 手机号
|
||||
// message 短信内容
|
||||
SendSMS(area, phoneNumber string, message string) int
|
||||
}
|
||||
|
||||
func NewSMSService() SMSService {
|
||||
return &GotoneService{}
|
||||
}
|
||||
Reference in New Issue
Block a user