From 8cede57a70677f108c31d4cd7893391007b0ae1e Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Fri, 21 Mar 2025 20:44:35 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/apis/line_pre_order.go | 25 ++++- app/admin/fronted/line_user.go | 24 ++--- app/admin/models/binance_account.go | 6 ++ app/admin/service/binance_account.go | 98 ++++++++++++++++++- app/admin/service/dto/line_pre_order.go | 2 + app/admin/service/dto/line_symbol.go | 10 +- app/admin/service/dto/line_user.go | 11 ++- app/admin/service/line_direction.go | 8 +- app/admin/service/line_pre_order.go | 60 +++++++++--- app/admin/service/line_symbol.go | 58 ++++++++++- app/admin/service/line_user.go | 23 +++-- app/jobs/jobs.go | 2 +- common/const/rediskey/control.go | 6 ++ common/helper/proxy.go | 5 +- .../service/sysservice/authservice/captcha.go | 59 +---------- config/extend.go | 10 +- config/settings.yml | 6 +- services/binanceservice/spotreset.go | 4 +- services/smsservice/gotone_service.go | 76 ++++++++++++++ services/smsservice/innopass_service.go | 44 +++++++++ services/smsservice/sms_service.go | 13 +++ 21 files changed, 430 insertions(+), 120 deletions(-) create mode 100644 common/const/rediskey/control.go create mode 100644 services/smsservice/gotone_service.go create mode 100644 services/smsservice/innopass_service.go create mode 100644 services/smsservice/sms_service.go diff --git a/app/admin/apis/line_pre_order.go b/app/admin/apis/line_pre_order.go index 1f18111..19b7c88 100644 --- a/app/admin/apis/line_pre_order.go +++ b/app/admin/apis/line_pre_order.go @@ -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 { diff --git a/app/admin/fronted/line_user.go b/app/admin/fronted/line_user.go index 38fac99..9176ceb 100644 --- a/app/admin/fronted/line_user.go +++ b/app/admin/fronted/line_user.go @@ -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, diff --git a/app/admin/models/binance_account.go b/app/admin/models/binance_account.go index c4fc0cf..1a9f1cd 100644 --- a/app/admin/models/binance_account.go +++ b/app/admin/models/binance_account.go @@ -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"` +} diff --git a/app/admin/service/binance_account.go b/app/admin/service/binance_account.go index dfc0b58..0ab8b2f 100644 --- a/app/admin/service/binance_account.go +++ b/app/admin/service/binance_account.go @@ -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 +} diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index f8b9f7a..82164b4 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -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 { diff --git a/app/admin/service/dto/line_symbol.go b/app/admin/service/dto/line_symbol.go index 22a8cf5..872cd7b 100644 --- a/app/admin/service/dto/line_symbol.go +++ b/app/admin/service/dto/line_symbol.go @@ -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 { diff --git a/app/admin/service/dto/line_user.go b/app/admin/service/dto/line_user.go index 9955836..a2361c5 100644 --- a/app/admin/service/dto/line_user.go +++ b/app/admin/service/dto/line_user.go @@ -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 diff --git a/app/admin/service/line_direction.go b/app/admin/service/line_direction.go index f88354b..1965c63 100644 --- a/app/admin/service/line_direction.go +++ b/app/admin/service/line_direction.go @@ -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("查看对象不存在或无权查看") diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 678a172..d8c2149 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -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) } } diff --git a/app/admin/service/line_symbol.go b/app/admin/service/line_symbol.go index 362e65f..461f3db 100644 --- a/app/admin/service/line_symbol.go +++ b/app/admin/service/line_symbol.go @@ -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) diff --git a/app/admin/service/line_user.go b/app/admin/service/line_user.go index 066951e..fbabfd5 100644 --- a/app/admin/service/line_user.go +++ b/app/admin/service/line_user.go @@ -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 diff --git a/app/jobs/jobs.go b/app/jobs/jobs.go index 54380f5..e85b2f5 100644 --- a/app/jobs/jobs.go +++ b/app/jobs/jobs.go @@ -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 } diff --git a/common/const/rediskey/control.go b/common/const/rediskey/control.go new file mode 100644 index 0000000..1ed7def --- /dev/null +++ b/common/const/rediskey/control.go @@ -0,0 +1,6 @@ +package rediskey + +const ( + //用户总资产 {exchange_type,user_id} + User_Total_Asset = "u_total_asset:%s:%v" +) diff --git a/common/helper/proxy.go b/common/helper/proxy.go index c76a95f..3a89447 100644 --- a/common/helper/proxy.go +++ b/common/helper/proxy.go @@ -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 diff --git a/common/service/sysservice/authservice/captcha.go b/common/service/sysservice/authservice/captcha.go index c22028c..3dc6951 100644 --- a/common/service/sysservice/authservice/captcha.go +++ b/common/service/sysservice/authservice/captcha.go @@ -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) diff --git a/config/extend.go b/config/extend.go index 52c01d9..1a41904 100644 --- a/config/extend.go +++ b/config/extend.go @@ -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"` diff --git a/config/settings.yml b/config/settings.yml index b9e5368..ac81484 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -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: diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index ab12be3..78e0534 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -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() diff --git a/services/smsservice/gotone_service.go b/services/smsservice/gotone_service.go new file mode 100644 index 0000000..c208aac --- /dev/null +++ b/services/smsservice/gotone_service.go @@ -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 +} diff --git a/services/smsservice/innopass_service.go b/services/smsservice/innopass_service.go new file mode 100644 index 0000000..bf8e581 --- /dev/null +++ b/services/smsservice/innopass_service.go @@ -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 +} diff --git a/services/smsservice/sms_service.go b/services/smsservice/sms_service.go new file mode 100644 index 0000000..61fcbf1 --- /dev/null +++ b/services/smsservice/sms_service.go @@ -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{} +}