Compare commits
1 Commits
4b28684fe4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e4286de50 |
@ -120,11 +120,6 @@ func (e LineApiUser) Insert(c *gin.Context) {
|
||||
// 设置创建人
|
||||
req.SetCreateBy(user.GetUserId(c))
|
||||
|
||||
if err := req.Valid(); err != nil {
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Insert(&req)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("创建api用户管理失败,\r\n失败信息 %s", err.Error()))
|
||||
@ -158,12 +153,6 @@ func (e LineApiUser) Update(c *gin.Context) {
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := req.Valid(); err != nil {
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
req.SetUpdateBy(user.GetUserId(c))
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
|
||||
@ -278,74 +267,3 @@ func (e LineApiUser) GetMainUser(c *gin.Context) {
|
||||
}
|
||||
e.OK(list, "操作成功")
|
||||
}
|
||||
|
||||
// GetUnBindApiUser 获取未绑定下反单api用户
|
||||
func (e LineApiUser) GetUnBindReverseApiUser(c *gin.Context) {
|
||||
s := service.LineApiUser{}
|
||||
req := dto.GetUnBindReverseReq{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req, binding.Query, binding.Form).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
datas := make([]dto.UnBindReverseResp, 0)
|
||||
|
||||
err = s.GetUnBindApiUser(&req, &datas)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("获取失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.OK(datas, "查询成功")
|
||||
}
|
||||
|
||||
// 获取反单api用户选项
|
||||
func (e LineApiUser) GetReverseApiOptions(c *gin.Context) {
|
||||
req := dto.GetReverseApiOptionsReq{}
|
||||
s := service.LineApiUser{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
list := make([]models.LineApiUser, 0)
|
||||
err = s.GetReverseApiOptions(&req, &list)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("获取失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
e.OK(list, "操作成功")
|
||||
}
|
||||
|
||||
// 获取所有启用的反单api用户
|
||||
func (e LineApiUser) GetReverseApiOptionsAll(c *gin.Context) {
|
||||
s := service.LineApiUser{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
list := make([]dto.LineApiUserOptionResp, 0)
|
||||
err = s.GetReverseApiOptionsAll(&list)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("获取失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
e.OK(list, "操作成功")
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||
@ -194,7 +193,6 @@ func (e LinePriceLimit) Delete(c *gin.Context) {
|
||||
e.OK(req.GetId(), "删除成功")
|
||||
}
|
||||
|
||||
// aicoin数据同步
|
||||
func (e LinePriceLimit) UpRange(c *gin.Context) {
|
||||
s := service.LinePriceLimit{}
|
||||
err := e.MakeContext(c).
|
||||
|
||||
@ -1,198 +0,0 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||
_ "github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/actions"
|
||||
)
|
||||
|
||||
type LineReverseOrder struct {
|
||||
api.Api
|
||||
}
|
||||
|
||||
// GetPage 获取反单下单列表列表
|
||||
// @Summary 获取反单下单列表列表
|
||||
// @Description 获取反单下单列表列表
|
||||
// @Tags 反单下单列表
|
||||
// @Param orderSn query string false "订单号"
|
||||
// @Param orderId query string false "币安订单号"
|
||||
// @Param followOrderSn query string false "跟随币安订单号"
|
||||
// @Param orderType query int false "订单类型 0-主单 1-止损单 2-加仓 3-减仓"
|
||||
// @Param positionSide query string false "持仓方向 LONG-多 SHORT-空"
|
||||
// @Param side query string false "买卖方向 SELL-卖 BUY-买"
|
||||
// @Param status query int false "状态 1-待下单 2-已下单 3-已成交 4-已平仓 5-已止损"
|
||||
// @Param pageSize query int false "页条数"
|
||||
// @Param pageIndex query int false "页码"
|
||||
// @Success 200 {object} response.Response{data=response.Page{list=[]models.LineReverseOrder}} "{"code": 200, "data": [...]}"
|
||||
// @Router /api/v1/line-reverse-order [get]
|
||||
// @Security Bearer
|
||||
func (e LineReverseOrder) GetPage(c *gin.Context) {
|
||||
req := dto.LineReverseOrderGetPageReq{}
|
||||
s := service.LineReverseOrder{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
list := make([]models.LineReverseOrder, 0)
|
||||
var count int64
|
||||
|
||||
err = s.GetPage(&req, p, &list, &count)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("获取反单下单列表失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
|
||||
}
|
||||
|
||||
// Get 获取反单下单列表
|
||||
// @Summary 获取反单下单列表
|
||||
// @Description 获取反单下单列表
|
||||
// @Tags 反单下单列表
|
||||
// @Param id path int false "id"
|
||||
// @Success 200 {object} response.Response{data=models.LineReverseOrder} "{"code": 200, "data": [...]}"
|
||||
// @Router /api/v1/line-reverse-order/{id} [get]
|
||||
// @Security Bearer
|
||||
func (e LineReverseOrder) Get(c *gin.Context) {
|
||||
req := dto.LineReverseOrderGetReq{}
|
||||
s := service.LineReverseOrder{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
var object models.LineReverseOrder
|
||||
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
err = s.Get(&req, p, &object)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("获取反单下单列表失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.OK( object, "查询成功")
|
||||
}
|
||||
|
||||
// Insert 创建反单下单列表
|
||||
// @Summary 创建反单下单列表
|
||||
// @Description 创建反单下单列表
|
||||
// @Tags 反单下单列表
|
||||
// @Accept application/json
|
||||
// @Product application/json
|
||||
// @Param data body dto.LineReverseOrderInsertReq true "data"
|
||||
// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
|
||||
// @Router /api/v1/line-reverse-order [post]
|
||||
// @Security Bearer
|
||||
func (e LineReverseOrder) Insert(c *gin.Context) {
|
||||
req := dto.LineReverseOrderInsertReq{}
|
||||
s := service.LineReverseOrder{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
// 设置创建人
|
||||
req.SetCreateBy(user.GetUserId(c))
|
||||
|
||||
err = s.Insert(&req)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("创建反单下单列表失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.OK(req.GetId(), "创建成功")
|
||||
}
|
||||
|
||||
// Update 修改反单下单列表
|
||||
// @Summary 修改反单下单列表
|
||||
// @Description 修改反单下单列表
|
||||
// @Tags 反单下单列表
|
||||
// @Accept application/json
|
||||
// @Product application/json
|
||||
// @Param id path int true "id"
|
||||
// @Param data body dto.LineReverseOrderUpdateReq true "body"
|
||||
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
|
||||
// @Router /api/v1/line-reverse-order/{id} [put]
|
||||
// @Security Bearer
|
||||
func (e LineReverseOrder) Update(c *gin.Context) {
|
||||
req := dto.LineReverseOrderUpdateReq{}
|
||||
s := service.LineReverseOrder{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
req.SetUpdateBy(user.GetUserId(c))
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
|
||||
err = s.Update(&req, p)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("修改反单下单列表失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
e.OK( req.GetId(), "修改成功")
|
||||
}
|
||||
|
||||
// Delete 删除反单下单列表
|
||||
// @Summary 删除反单下单列表
|
||||
// @Description 删除反单下单列表
|
||||
// @Tags 反单下单列表
|
||||
// @Param data body dto.LineReverseOrderDeleteReq true "body"
|
||||
// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
|
||||
// @Router /api/v1/line-reverse-order [delete]
|
||||
// @Security Bearer
|
||||
func (e LineReverseOrder) Delete(c *gin.Context) {
|
||||
s := service.LineReverseOrder{}
|
||||
req := dto.LineReverseOrderDeleteReq{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// req.SetUpdateBy(user.GetUserId(c))
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
|
||||
err = s.Remove(&req, p)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("删除反单下单列表失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
e.OK( req.GetId(), "删除成功")
|
||||
}
|
||||
@ -1,255 +0,0 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||
_ "github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/actions"
|
||||
)
|
||||
|
||||
type LineReversePosition struct {
|
||||
api.Api
|
||||
}
|
||||
|
||||
// GetPage 获取反单管理-仓位列表
|
||||
// @Summary 获取反单管理-仓位列表
|
||||
// @Description 获取反单管理-仓位列表
|
||||
// @Tags 反单管理-仓位
|
||||
// @Param apiId query int64 false "api_id"
|
||||
// @Param reverseApiId query int64 false "反单api_id"
|
||||
// @Param side query string false "买卖方向 BUY SELL"
|
||||
// @Param positionSide query string false "持仓方向 LONG SHORT"
|
||||
// @Param symbol query string false "交易对"
|
||||
// @Param pageSize query int false "页条数"
|
||||
// @Param pageIndex query int false "页码"
|
||||
// @Success 200 {object} response.Response{data=response.Page{list=[]models.LineReversePosition}} "{"code": 200, "data": [...]}"
|
||||
// @Router /api/v1/line-reverse-position [get]
|
||||
// @Security Bearer
|
||||
func (e LineReversePosition) GetPage(c *gin.Context) {
|
||||
req := dto.LineReversePositionGetPageReq{}
|
||||
s := service.LineReversePosition{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
list := make([]dto.LineReversePositionListResp, 0)
|
||||
var count int64
|
||||
|
||||
err = s.GetPage(&req, p, &list, &count)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("获取反单管理-仓位失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
|
||||
}
|
||||
|
||||
// Get 获取反单管理-仓位
|
||||
// @Summary 获取反单管理-仓位
|
||||
// @Description 获取反单管理-仓位
|
||||
// @Tags 反单管理-仓位
|
||||
// @Param id path int false "id"
|
||||
// @Success 200 {object} response.Response{data=models.LineReversePosition} "{"code": 200, "data": [...]}"
|
||||
// @Router /api/v1/line-reverse-position/{id} [get]
|
||||
// @Security Bearer
|
||||
func (e LineReversePosition) Get(c *gin.Context) {
|
||||
req := dto.LineReversePositionGetReq{}
|
||||
s := service.LineReversePosition{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
var object models.LineReversePosition
|
||||
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
err = s.Get(&req, p, &object)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("获取反单管理-仓位失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.OK(object, "查询成功")
|
||||
}
|
||||
|
||||
// Insert 创建反单管理-仓位
|
||||
// @Summary 创建反单管理-仓位
|
||||
// @Description 创建反单管理-仓位
|
||||
// @Tags 反单管理-仓位
|
||||
// @Accept application/json
|
||||
// @Product application/json
|
||||
// @Param data body dto.LineReversePositionInsertReq true "data"
|
||||
// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
|
||||
// @Router /api/v1/line-reverse-position [post]
|
||||
// @Security Bearer
|
||||
func (e LineReversePosition) Insert(c *gin.Context) {
|
||||
req := dto.LineReversePositionInsertReq{}
|
||||
s := service.LineReversePosition{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
// 设置创建人
|
||||
req.SetCreateBy(user.GetUserId(c))
|
||||
|
||||
err = s.Insert(&req)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("创建反单管理-仓位失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.OK(req.GetId(), "创建成功")
|
||||
}
|
||||
|
||||
// Update 修改反单管理-仓位
|
||||
// @Summary 修改反单管理-仓位
|
||||
// @Description 修改反单管理-仓位
|
||||
// @Tags 反单管理-仓位
|
||||
// @Accept application/json
|
||||
// @Product application/json
|
||||
// @Param id path int true "id"
|
||||
// @Param data body dto.LineReversePositionUpdateReq true "body"
|
||||
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
|
||||
// @Router /api/v1/line-reverse-position/{id} [put]
|
||||
// @Security Bearer
|
||||
func (e LineReversePosition) Update(c *gin.Context) {
|
||||
req := dto.LineReversePositionUpdateReq{}
|
||||
s := service.LineReversePosition{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
req.SetUpdateBy(user.GetUserId(c))
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
|
||||
err = s.Update(&req, p)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("修改反单管理-仓位失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
e.OK(req.GetId(), "修改成功")
|
||||
}
|
||||
|
||||
// Delete 删除反单管理-仓位
|
||||
// @Summary 删除反单管理-仓位
|
||||
// @Description 删除反单管理-仓位
|
||||
// @Tags 反单管理-仓位
|
||||
// @Param data body dto.LineReversePositionDeleteReq true "body"
|
||||
// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
|
||||
// @Router /api/v1/line-reverse-position [delete]
|
||||
// @Security Bearer
|
||||
func (e LineReversePosition) Delete(c *gin.Context) {
|
||||
s := service.LineReversePosition{}
|
||||
req := dto.LineReversePositionDeleteReq{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// req.SetUpdateBy(user.GetUserId(c))
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
|
||||
err = s.Remove(&req, p)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("删除反单管理-仓位失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
e.OK(req.GetId(), "删除成功")
|
||||
}
|
||||
|
||||
// ClosePosition 平仓
|
||||
func (e LineReversePosition) ClosePosition(c *gin.Context) {
|
||||
req := dto.LineReversePositionCloseReq{}
|
||||
s := service.LineReversePosition{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
userId := user.GetUserId(c)
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
|
||||
err = s.ClosePosition(&req, p, userId)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("平仓失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
e.OK(nil, "平仓成功")
|
||||
}
|
||||
|
||||
// ClosePositionBatch 批量平仓
|
||||
func (e LineReversePosition) ClosePositionBatch(c *gin.Context) {
|
||||
req := dto.LineReversePositionCloseBatchReq{}
|
||||
s := service.LineReversePosition{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
userId := user.GetUserId(c)
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
errs := make([]string, 0)
|
||||
|
||||
err = s.ClosePositionBatch(&req, p, userId, &errs)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("批量平仓失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
content := strings.Join(errs, "</br>")
|
||||
|
||||
e.OK(content, "批量平仓部分失败")
|
||||
return
|
||||
}
|
||||
e.OK(nil, "批量平仓成功")
|
||||
}
|
||||
@ -1,158 +0,0 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||
_ "github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/actions"
|
||||
)
|
||||
|
||||
type LineReverseSetting struct {
|
||||
api.Api
|
||||
}
|
||||
|
||||
// GetPage 获取反单下单配置列表
|
||||
// @Summary 获取反单下单配置列表
|
||||
// @Description 获取反单下单配置列表
|
||||
// @Tags 反单下单配置
|
||||
// @Param pageSize query int false "页条数"
|
||||
// @Param pageIndex query int false "页码"
|
||||
// @Success 200 {object} response.Response{data=response.Page{list=[]models.LineReverseSetting}} "{"code": 200, "data": [...]}"
|
||||
// @Router /api/v1/line-reverse-setting [get]
|
||||
// @Security Bearer
|
||||
func (e LineReverseSetting) GetPage(c *gin.Context) {
|
||||
req := dto.LineReverseSettingGetPageReq{}
|
||||
s := service.LineReverseSetting{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
list := make([]models.LineReverseSetting, 0)
|
||||
var count int64
|
||||
|
||||
err = s.GetPage(&req, p, &list, &count)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("获取反单下单配置失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
|
||||
}
|
||||
|
||||
// Get 获取反单下单配置
|
||||
// @Summary 获取反单下单配置
|
||||
// @Description 获取反单下单配置
|
||||
// @Tags 反单下单配置
|
||||
// @Param id path int false "id"
|
||||
// @Success 200 {object} response.Response{data=models.LineReverseSetting} "{"code": 200, "data": [...]}"
|
||||
// @Router /api/v1/line-reverse-setting/{id} [get]
|
||||
// @Security Bearer
|
||||
func (e LineReverseSetting) Get(c *gin.Context) {
|
||||
req := dto.LineReverseSettingGetReq{}
|
||||
s := service.LineReverseSetting{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
var object models.LineReverseSetting
|
||||
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
err = s.Get(&req, p, &object)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("获取反单下单配置失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.OK(object, "查询成功")
|
||||
}
|
||||
|
||||
// Insert 创建反单下单配置
|
||||
// @Summary 创建反单下单配置
|
||||
// @Description 创建反单下单配置
|
||||
// @Tags 反单下单配置
|
||||
// @Accept application/json
|
||||
// @Product application/json
|
||||
// @Param data body dto.LineReverseSettingInsertReq true "data"
|
||||
// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
|
||||
// @Router /api/v1/line-reverse-setting [post]
|
||||
// @Security Bearer
|
||||
func (e LineReverseSetting) Insert(c *gin.Context) {
|
||||
req := dto.LineReverseSettingInsertReq{}
|
||||
s := service.LineReverseSetting{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
// 设置创建人
|
||||
req.SetCreateBy(user.GetUserId(c))
|
||||
|
||||
err = s.Insert(&req)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("创建反单下单配置失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e.OK(req.GetId(), "创建成功")
|
||||
}
|
||||
|
||||
// Update 修改反单下单配置
|
||||
// @Summary 修改反单下单配置
|
||||
// @Description 修改反单下单配置
|
||||
// @Tags 反单下单配置
|
||||
// @Accept application/json
|
||||
// @Product application/json
|
||||
// @Param id path int true "id"
|
||||
// @Param data body dto.LineReverseSettingUpdateReq true "body"
|
||||
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
|
||||
// @Router /api/v1/line-reverse-setting/{id} [put]
|
||||
// @Security Bearer
|
||||
func (e LineReverseSetting) Update(c *gin.Context) {
|
||||
req := dto.LineReverseSettingUpdateReq{}
|
||||
s := service.LineReverseSetting{}
|
||||
err := e.MakeContext(c).
|
||||
MakeOrm().
|
||||
Bind(&req).
|
||||
MakeService(&s.Service).
|
||||
Errors
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Error(500, err, err.Error())
|
||||
return
|
||||
}
|
||||
req.SetUpdateBy(user.GetUserId(c))
|
||||
p := actions.GetPermissionFromContext(c)
|
||||
|
||||
err = s.Update(&req, p)
|
||||
if err != nil {
|
||||
e.Error(500, err, fmt.Sprintf("修改反单下单配置失败,\r\n失败信息 %s", err.Error()))
|
||||
return
|
||||
}
|
||||
e.OK(req.GetId(), "修改成功")
|
||||
}
|
||||
@ -2,31 +2,29 @@ package models
|
||||
|
||||
import (
|
||||
"go-admin/common/models"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LineApiUser struct {
|
||||
models.Model
|
||||
|
||||
ExchangeType string `json:"exchangeType" gorm:"type:varchar(20);comment:交易所类型(字典 exchange_type)"`
|
||||
UserId int64 `json:"userId" gorm:"type:int unsigned;comment:用户id"`
|
||||
ApiName string `json:"apiName" gorm:"type:varchar(255);comment:api用户名"`
|
||||
ApiKey string `json:"apiKey" gorm:"type:varchar(255);comment:apiKey"`
|
||||
ApiSecret string `json:"apiSecret" gorm:"type:varchar(255);comment:apiSecret"`
|
||||
IpAddress string `json:"ipAddress" gorm:"type:varchar(255);comment:代理地址"`
|
||||
UserPass string `json:"userPass" gorm:"type:varchar(255);comment:代码账号密码"`
|
||||
Affiliation int64 `json:"affiliation" gorm:"type:int;comment:归属:1=现货,2=合约,3=现货合约"`
|
||||
AdminShow int64 `json:"adminShow" gorm:"type:int;comment:是否超管可见:1=是,0=否"`
|
||||
Site string `json:"site" gorm:"type:enum('1','2','3');comment:允许下单的方向:1=多;2=空;3=多空"`
|
||||
Subordinate string `json:"subordinate" gorm:"type:enum('0','1','2');comment:从属关系:0=未绑定关系;1=主账号;2=副帐号"`
|
||||
GroupId int64 `json:"groupId" gorm:"type:int unsigned;comment:所属组id"`
|
||||
OpenStatus int64 `json:"openStatus" gorm:"type:int unsigned;comment:开启状态 0=关闭 1=开启"`
|
||||
ReverseStatus int `json:"reverseStatus" gorm:"type:tinyint unsigned;comment:反向开仓:1=开启;2=关闭"`
|
||||
ReverseApiId int `json:"reverseApiId" gorm:"type:bigint;comment:反向apiId"`
|
||||
OrderProportion decimal.Decimal `json:"orderProportion" gorm:"type:decimal(10,2);comment:委托比例"`
|
||||
SpotLastTime string `json:"spotLastTime" gorm:"-"` //现货websocket最后通信时间
|
||||
FuturesLastTime string `json:"futuresLastTime" gorm:"-"` //合约websocket最后通信时间
|
||||
ExchangeType string `json:"exchangeType" gorm:"type:varchar(20);comment:交易所类型(字典 exchange_type)"`
|
||||
UserId int64 `json:"userId" gorm:"type:int unsigned;comment:用户id"`
|
||||
ApiName string `json:"apiName" gorm:"type:varchar(255);comment:api用户名"`
|
||||
ApiKey string `json:"apiKey" gorm:"type:varchar(255);comment:apiKey"`
|
||||
ApiSecret string `json:"apiSecret" gorm:"type:varchar(255);comment:apiSecret"`
|
||||
IpAddress string `json:"ipAddress" gorm:"type:varchar(255);comment:代理地址"`
|
||||
UserPass string `json:"userPass" gorm:"type:varchar(255);comment:代码账号密码"`
|
||||
// AdminId int64 `json:"adminId" gorm:"type:int unsigned;comment:管理员id"`
|
||||
Affiliation int64 `json:"affiliation" gorm:"type:int;comment:归属:1=现货,2=合约,3=现货合约"`
|
||||
AdminShow int64 `json:"adminShow" gorm:"type:int;comment:是否超管可见:1=是,0=否"`
|
||||
Site string `json:"site" gorm:"type:enum('1','2','3');comment:允许下单的方向:1=多;2=空;3=多空"`
|
||||
Subordinate string `json:"subordinate" gorm:"type:enum('0','1','2');comment:从属关系:0=未绑定关系;1=主账号;2=副帐号"`
|
||||
GroupId int64 `json:"groupId" gorm:"type:int unsigned;comment:所属组id"`
|
||||
OpenStatus int64 `json:"openStatus" gorm:"type:int unsigned;comment:开启状态 0=关闭 1=开启"`
|
||||
|
||||
SpotLastTime *time.Time `json:"spotLastTime" gorm:"-"` //现货websocket最后通信时间
|
||||
FuturesLastTime *time.Time `json:"futuresLastTime" gorm:"-"` //合约websocket最后通信时间
|
||||
models.ModelTime
|
||||
models.ControlBy
|
||||
}
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go-admin/common/models"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type LineReverseOrder struct {
|
||||
models.Model
|
||||
|
||||
PositionId int `json:"positionId" gorm:"type:bigint;comment:仓位id"`
|
||||
ApiId int `json:"apiId" gorm:"type:bigint;comment:api id"`
|
||||
Category int `json:"category" gorm:"type:tinyint;comment:分类 0-原始订单 1-反单"`
|
||||
|
||||
OrderSn string `json:"orderSn" gorm:"type:varchar(50);comment:订单号 0-主单 1-止盈 2-止损"`
|
||||
OrderId string `json:"orderId" gorm:"type:varchar(50);comment:币安订单号"`
|
||||
FollowOrderSn string `json:"followOrderSn" gorm:"type:varchar(50);comment:跟随币安订单号"`
|
||||
Symbol string `json:"symbol" gorm:"type:varchar(20);comment:交易对"`
|
||||
Type string `json:"type" gorm:"type:varchar(20);comment:交易类型"`
|
||||
OrderType int `json:"orderType" gorm:"type:tinyint;comment:订单类型 0-主单 1-止盈 2-止损 3-减仓单 4-平仓单"`
|
||||
BuyPrice decimal.Decimal `json:"buyPrice" gorm:"type:decimal(18,8);comment:购买金额"`
|
||||
Price decimal.Decimal `json:"price" gorm:"type:decimal(18,8);comment:委托价格"`
|
||||
TotalNum decimal.Decimal `json:"totalNum" gorm:"type:decimal(18,8);comment:总成交数量"`
|
||||
PriceU decimal.Decimal `json:"priceU" gorm:"type:decimal(18,8);comment:委托价格(U)"`
|
||||
FinalPrice decimal.Decimal `json:"finalPrice" gorm:"type:decimal(18,8);comment:实际成交价"`
|
||||
PositionSide string `json:"positionSide" gorm:"type:varchar(10);comment:持仓方向 LONG-多 SHORT-空"`
|
||||
Side string `json:"side" gorm:"type:varchar(10);comment:买卖方向 SELL-卖 BUY-买"`
|
||||
SignPrice decimal.Decimal `json:"signPrice" gorm:"type:decimal(18,8);comment:行情价"`
|
||||
TriggerTime *time.Time `json:"triggerTime" gorm:"type:datetime;comment:触发时间"`
|
||||
Status int `json:"status" gorm:"type:tinyint;comment:状态 1-待下单 2-已下单 3-已成交 6-已取消 7-已过期 8-已失败"`
|
||||
IsAddPosition int `json:"isAddPosition" gorm:"type:tinyint;comment:是否增加仓位 1-否 2-是"`
|
||||
IsReduce int `json:"isReduce" gorm:"type:tinyint;comment:是否减少仓位 1-否 2-是"`
|
||||
Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"`
|
||||
models.ModelTime
|
||||
models.ControlBy
|
||||
}
|
||||
|
||||
func (LineReverseOrder) TableName() string {
|
||||
return "line_reverse_order"
|
||||
}
|
||||
|
||||
func (e *LineReverseOrder) Generate() models.ActiveRecord {
|
||||
o := *e
|
||||
return &o
|
||||
}
|
||||
|
||||
func (e *LineReverseOrder) GetId() interface{} {
|
||||
return e.Id
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"go-admin/common/models"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type LineReversePosition struct {
|
||||
models.Model
|
||||
|
||||
PositionNo string `json:"positionNo" gorm:"type:varchar(18);comment:仓位编号"`
|
||||
ApiId int `json:"apiId" gorm:"type:bigint;comment:api_id"`
|
||||
TotalAmount decimal.Decimal `json:"totalAmount" gorm:"type:decimal(18,8);comment:总仓位"`
|
||||
Amount decimal.Decimal `json:"amount" gorm:"type:decimal(18,8);comment:仓位"`
|
||||
ReverseApiId int `json:"reverseApiId" gorm:"type:bigint;comment:反单api_id"`
|
||||
TotalReverseAmount decimal.Decimal `json:"totalReverseAmount" gorm:"type:decimal(18,8);comment:总反单仓位"`
|
||||
ReverseAmount decimal.Decimal `json:"reverseAmount" gorm:"type:decimal(18,8);comment:反单仓位"`
|
||||
Side string `json:"side" gorm:"type:varchar(10);comment:买卖方向 BUY SELL"`
|
||||
PositionSide string `json:"positionSide" gorm:"type:varchar(10);comment:持仓方向 LONG SHORT"`
|
||||
Symbol string `json:"symbol" gorm:"type:varchar(20);comment:交易对"`
|
||||
Status int `json:"status" gorm:"type:tinyint;comment:仓位状态 1-已开仓 2-已平仓"`
|
||||
ReverseStatus int `json:"reverseStatus" gorm:"type:tinyint;comment:反单仓位状态 1-已开仓 2-已平仓"`
|
||||
AveragePrice decimal.Decimal `json:"averagePrice" gorm:"type:decimal(18,8);comment:主单平均价格"`
|
||||
ReverseAveragePrice decimal.Decimal `json:"reverseAveragePrice" gorm:"type:decimal(18,8);comment:反单平均价格"`
|
||||
models.ModelTime
|
||||
models.ControlBy
|
||||
}
|
||||
|
||||
func (LineReversePosition) TableName() string {
|
||||
return "line_reverse_position"
|
||||
}
|
||||
|
||||
func (e *LineReversePosition) Generate() models.ActiveRecord {
|
||||
o := *e
|
||||
return &o
|
||||
}
|
||||
|
||||
func (e *LineReversePosition) GetId() interface{} {
|
||||
return e.Id
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"go-admin/common/models"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type LineReverseSetting struct {
|
||||
models.Model
|
||||
|
||||
ReverseOrderType string `json:"reverseOrderType" redis:"reverseOrderType" gorm:"type:varchar(10);comment:反单下单类型 LIMIT-限价 MARKET-市价"`
|
||||
ReversePremiumRatio decimal.Decimal `json:"reversePremiumRatio" redis:"reversePremiumRatio" gorm:"type:decimal(10,2);comment:溢价百分比"`
|
||||
TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" redis:"takeProfitRatio" gorm:"type:decimal(10,2);comment:止盈百分比"`
|
||||
StopLossRatio decimal.Decimal `json:"stopLossRatio" redis:"stopLossRatio" gorm:"type:decimal(10,2);comment:止损百分比"`
|
||||
models.ModelTime
|
||||
models.ControlBy
|
||||
}
|
||||
|
||||
func (LineReverseSetting) TableName() string {
|
||||
return "line_reverse_setting"
|
||||
}
|
||||
|
||||
func (e *LineReverseSetting) Generate() models.ActiveRecord {
|
||||
o := *e
|
||||
return &o
|
||||
}
|
||||
|
||||
func (e *LineReverseSetting) GetId() interface{} {
|
||||
return e.Id
|
||||
}
|
||||
@ -27,11 +27,5 @@ func registerLineApiUserRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMi
|
||||
r.POST("bind", api.Bind) //绑定从属关系
|
||||
r.POST("getUser", api.GetUser) //获取未绑定的用户
|
||||
r.POST("getMainUser", api.GetMainUser) //获取获取主账号的用户
|
||||
|
||||
r.GET("unbind-reverse", api.GetUnBindReverseApiUser) //获取未绑定下反单用户
|
||||
|
||||
r.GET("reverse-options", api.GetReverseApiOptions) //获取可用反单api用户
|
||||
|
||||
r.GET("reverse-options-all", api.GetReverseApiOptionsAll) //获取全部启用的反单api用户
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||
|
||||
"go-admin/app/admin/apis"
|
||||
"go-admin/common/middleware"
|
||||
"go-admin/common/actions"
|
||||
)
|
||||
|
||||
func init() {
|
||||
routerCheckRole = append(routerCheckRole, registerLineReverseOrderRouter)
|
||||
}
|
||||
|
||||
// registerLineReverseOrderRouter
|
||||
func registerLineReverseOrderRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
||||
api := apis.LineReverseOrder{}
|
||||
r := v1.Group("/line-reverse-order").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
|
||||
{
|
||||
r.GET("", actions.PermissionAction(), api.GetPage)
|
||||
r.GET("/:id", actions.PermissionAction(), api.Get)
|
||||
r.POST("", api.Insert)
|
||||
r.PUT("/:id", actions.PermissionAction(), api.Update)
|
||||
r.DELETE("", api.Delete)
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||
|
||||
"go-admin/app/admin/apis"
|
||||
"go-admin/common/actions"
|
||||
"go-admin/common/middleware"
|
||||
)
|
||||
|
||||
func init() {
|
||||
routerCheckRole = append(routerCheckRole, registerLineReversePositionRouter)
|
||||
}
|
||||
|
||||
// registerLineReversePositionRouter
|
||||
func registerLineReversePositionRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
||||
api := apis.LineReversePosition{}
|
||||
r := v1.Group("/line-reverse-position").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
|
||||
{
|
||||
r.GET("", actions.PermissionAction(), api.GetPage)
|
||||
r.GET("/:id", actions.PermissionAction(), api.Get)
|
||||
r.POST("", api.Insert)
|
||||
r.PUT("/:id", actions.PermissionAction(), api.Update)
|
||||
r.DELETE("", api.Delete)
|
||||
|
||||
r.PUT("close/:id", actions.PermissionAction(), api.ClosePosition)
|
||||
r.PUT("close-batch", actions.PermissionAction(), api.ClosePositionBatch)
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||
|
||||
"go-admin/app/admin/apis"
|
||||
"go-admin/common/actions"
|
||||
"go-admin/common/middleware"
|
||||
)
|
||||
|
||||
func init() {
|
||||
routerCheckRole = append(routerCheckRole, registerLineReverseSettingRouter)
|
||||
}
|
||||
|
||||
// registerLineReverseSettingRouter
|
||||
func registerLineReverseSettingRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
||||
api := apis.LineReverseSetting{}
|
||||
r := v1.Group("/line-reverse-setting").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
|
||||
{
|
||||
r.GET("", actions.PermissionAction(), api.GetPage)
|
||||
r.GET("/:id", actions.PermissionAction(), api.Get)
|
||||
r.POST("", api.Insert)
|
||||
r.PUT("/:id", actions.PermissionAction(), api.Update)
|
||||
// r.DELETE("", api.Delete)
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,9 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/common/dto"
|
||||
common "go-admin/common/models"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type LineApiUserGetPageReq struct {
|
||||
@ -17,9 +14,9 @@ type LineApiUserGetPageReq struct {
|
||||
}
|
||||
|
||||
type LineApiUserOrder struct {
|
||||
Id string `form:"idOrder" search:"type:order;column:id;table:line_api_user"`
|
||||
UserId string `form:"userIdOrder" search:"type:order;column:user_id;table:line_api_user"`
|
||||
JysId string `form:"jysIdOrder" search:"type:order;column:jys_id;table:line_api_user"`
|
||||
Id string `form:"idOrder" search:"type:order;column:id;table:line_api_user"`
|
||||
UserId string `form:"userIdOrder" search:"type:order;column:user_id;table:line_api_user"`
|
||||
// JysId string `form:"jysIdOrder" search:"type:order;column:jys_id;table:line_api_user"`
|
||||
ApiName string `form:"apiNameOrder" search:"type:order;column:api_name;table:line_api_user"`
|
||||
ApiKey string `form:"apiKeyOrder" search:"type:order;column:api_key;table:line_api_user"`
|
||||
ApiSecret string `form:"apiSecretOrder" search:"type:order;column:api_secret;table:line_api_user"`
|
||||
@ -44,66 +41,44 @@ func (m *LineApiUserGetPageReq) GetNeedSearch() interface{} {
|
||||
}
|
||||
|
||||
type LineApiUserInsertReq struct {
|
||||
Id int `json:"-" comment:"id"` // id
|
||||
ExchangeType string `json:"exchangeType" comment:"交易所code"`
|
||||
UserId int64 `json:"userId" comment:"用户id"`
|
||||
JysId int64 `json:"jysId" comment:"关联交易所账号id"`
|
||||
ApiName string `json:"apiName" comment:"api用户名"`
|
||||
ApiKey string `json:"apiKey" comment:"apiKey"`
|
||||
ApiSecret string `json:"apiSecret" comment:"apiSecret"`
|
||||
IpAddress string `json:"ipAddress" comment:"代理地址"`
|
||||
UserPass string `json:"userPass" comment:"代码账号密码"`
|
||||
Affiliation int64 `json:"affiliation" comment:"归属:1=现货,2=合约,3=现货合约"`
|
||||
AdminShow int64 `json:"adminShow" comment:"是否超管可见:1=是,0=否"`
|
||||
Site string `json:"site" comment:"允许下单的方向:1=多;2=空;3=多空"`
|
||||
Subordinate string `json:"subordinate" comment:"从属关系:0=未绑定关系;1=主账号;2=副帐号"`
|
||||
GroupId int64 `json:"groupId" comment:"所属组id"`
|
||||
OpenStatus int64 `json:"openStatus" comment:"开启状态 0=关闭 1=开启"`
|
||||
ReverseStatus int `json:"reverseStatus" comment:"反向下单状态 1-开启 2-关闭"`
|
||||
ReverseApiId int `json:"reverseApiId" comment:"反向下单apiId"`
|
||||
OrderProportion decimal.Decimal `json:"orderProportion" comment:"委托比例"`
|
||||
Id int `json:"-" comment:"id"` // id
|
||||
ExchangeType string `json:"exchangeType" comment:"交易所code"`
|
||||
UserId int64 `json:"userId" comment:"用户id"`
|
||||
// JysId int64 `json:"jysId" comment:"关联交易所账号id"`
|
||||
ApiName string `json:"apiName" comment:"api用户名"`
|
||||
ApiKey string `json:"apiKey" comment:"apiKey"`
|
||||
ApiSecret string `json:"apiSecret" comment:"apiSecret"`
|
||||
IpAddress string `json:"ipAddress" comment:"代理地址"`
|
||||
UserPass string `json:"userPass" comment:"代码账号密码"`
|
||||
AdminId int64 `json:"adminId" comment:"管理员id"`
|
||||
Affiliation int64 `json:"affiliation" comment:"归属:1=现货,2=合约,3=现货合约"`
|
||||
AdminShow int64 `json:"adminShow" comment:"是否超管可见:1=是,0=否"`
|
||||
Site string `json:"site" comment:"允许下单的方向:1=多;2=空;3=多空"`
|
||||
Subordinate string `json:"subordinate" comment:"从属关系:0=未绑定关系;1=主账号;2=副帐号"`
|
||||
GroupId int64 `json:"groupId" comment:"所属组id"`
|
||||
OpenStatus int64 `json:"openStatus" comment:"开启状态 0=关闭 1=开启"`
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
func (s *LineApiUserInsertReq) Valid() error {
|
||||
if s.ReverseStatus == 1 {
|
||||
if s.OrderProportion.Cmp(decimal.Zero) <= 0 {
|
||||
return errors.New("委托比例必须大于0")
|
||||
}
|
||||
|
||||
if s.ReverseStatus == 1 && s.ReverseApiId == 0 {
|
||||
return errors.New("反向下单api不能为空")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LineApiUserInsertReq) Generate(model *models.LineApiUser) {
|
||||
if s.Id == 0 {
|
||||
model.Model = common.Model{Id: s.Id}
|
||||
}
|
||||
model.ExchangeType = s.ExchangeType
|
||||
model.UserId = s.UserId
|
||||
// model.JysId = s.JysId
|
||||
model.ApiName = s.ApiName
|
||||
model.ApiKey = s.ApiKey
|
||||
model.ApiSecret = s.ApiSecret
|
||||
model.IpAddress = s.IpAddress
|
||||
model.UserPass = s.UserPass
|
||||
// model.AdminId = s.AdminId
|
||||
model.Affiliation = s.Affiliation
|
||||
model.AdminShow = s.AdminShow
|
||||
model.Site = s.Site
|
||||
model.Subordinate = s.Subordinate
|
||||
model.GroupId = s.GroupId
|
||||
model.OpenStatus = s.OpenStatus
|
||||
model.ReverseStatus = s.ReverseStatus
|
||||
model.OrderProportion = s.OrderProportion
|
||||
|
||||
if model.ReverseStatus == 1 {
|
||||
model.ReverseApiId = s.ReverseApiId
|
||||
} else {
|
||||
model.ReverseApiId = 0
|
||||
}
|
||||
|
||||
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
||||
}
|
||||
|
||||
@ -112,66 +87,44 @@ func (s *LineApiUserInsertReq) GetId() interface{} {
|
||||
}
|
||||
|
||||
type LineApiUserUpdateReq struct {
|
||||
Id int `uri:"id" comment:"id"` // id
|
||||
ExchangeType string `json:"exchangeType" comment:"交易所code"`
|
||||
UserId int64 `json:"userId" comment:"用户id"`
|
||||
JysId int64 `json:"jysId" comment:"关联交易所账号id"`
|
||||
ApiName string `json:"apiName" comment:"api用户名"`
|
||||
ApiKey string `json:"apiKey" comment:"apiKey"`
|
||||
ApiSecret string `json:"apiSecret" comment:"apiSecret"`
|
||||
IpAddress string `json:"ipAddress" comment:"代理地址"`
|
||||
UserPass string `json:"userPass" comment:"代码账号密码"`
|
||||
Affiliation int64 `json:"affiliation" comment:"归属:1=现货,2=合约,3=现货合约"`
|
||||
AdminShow int64 `json:"adminShow" comment:"是否超管可见:1=是,0=否"`
|
||||
Site string `json:"site" comment:"允许下单的方向:1=多;2=空;3=多空"`
|
||||
Subordinate string `json:"subordinate" comment:"从属关系:0=未绑定关系;1=主账号;2=副帐号"`
|
||||
GroupId int64 `json:"groupId" comment:"所属组id"`
|
||||
OpenStatus int64 `json:"openStatus" comment:"开启状态 0=关闭 1=开启"`
|
||||
ReverseStatus int `json:"reverseStatus" comment:"反向下单状态 1-开启 2-关闭"`
|
||||
ReverseApiId int `json:"reverseApiId" comment:"反向下单apiId"`
|
||||
OrderProportion decimal.Decimal `json:"orderProportion" comment:"委托比例"`
|
||||
Id int `uri:"id" comment:"id"` // id
|
||||
ExchangeType string `json:"exchangeType" comment:"交易所code"`
|
||||
UserId int64 `json:"userId" comment:"用户id"`
|
||||
// JysId int64 `json:"jysId" comment:"关联交易所账号id"`
|
||||
ApiName string `json:"apiName" comment:"api用户名"`
|
||||
ApiKey string `json:"apiKey" comment:"apiKey"`
|
||||
ApiSecret string `json:"apiSecret" comment:"apiSecret"`
|
||||
IpAddress string `json:"ipAddress" comment:"代理地址"`
|
||||
UserPass string `json:"userPass" comment:"代码账号密码"`
|
||||
// AdminId int64 `json:"adminId" comment:"管理员id"`
|
||||
Affiliation int64 `json:"affiliation" comment:"归属:1=现货,2=合约,3=现货合约"`
|
||||
AdminShow int64 `json:"adminShow" comment:"是否超管可见:1=是,0=否"`
|
||||
Site string `json:"site" comment:"允许下单的方向:1=多;2=空;3=多空"`
|
||||
Subordinate string `json:"subordinate" comment:"从属关系:0=未绑定关系;1=主账号;2=副帐号"`
|
||||
GroupId int64 `json:"groupId" comment:"所属组id"`
|
||||
OpenStatus int64 `json:"openStatus" comment:"开启状态 0=关闭 1=开启"`
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
func (s *LineApiUserUpdateReq) Valid() error {
|
||||
if s.ReverseStatus == 1 {
|
||||
if s.OrderProportion.Cmp(decimal.Zero) <= 0 {
|
||||
return errors.New("委托比例必须大于0")
|
||||
}
|
||||
|
||||
if s.ReverseStatus == 1 && s.ReverseApiId == 0 {
|
||||
return errors.New("反向下单api不能为空")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LineApiUserUpdateReq) Generate(model *models.LineApiUser) {
|
||||
if s.Id == 0 {
|
||||
model.Model = common.Model{Id: s.Id}
|
||||
}
|
||||
model.ExchangeType = s.ExchangeType
|
||||
model.UserId = s.UserId
|
||||
// model.JysId = s.JysId
|
||||
model.ApiName = s.ApiName
|
||||
model.ApiKey = s.ApiKey
|
||||
model.ApiSecret = s.ApiSecret
|
||||
model.IpAddress = s.IpAddress
|
||||
model.UserPass = s.UserPass
|
||||
// model.AdminId = s.AdminId
|
||||
model.Affiliation = s.Affiliation
|
||||
model.AdminShow = s.AdminShow
|
||||
model.Site = s.Site
|
||||
model.Subordinate = s.Subordinate
|
||||
model.GroupId = s.GroupId
|
||||
model.OpenStatus = s.OpenStatus
|
||||
model.ReverseStatus = s.ReverseStatus
|
||||
model.OrderProportion = s.OrderProportion
|
||||
|
||||
if model.ReverseStatus == 1 {
|
||||
model.ReverseApiId = s.ReverseApiId
|
||||
} else {
|
||||
model.ReverseApiId = 0
|
||||
}
|
||||
|
||||
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
||||
}
|
||||
|
||||
@ -206,26 +159,3 @@ type LineApiUserBindSubordinateReq struct {
|
||||
type GetMainUserReq struct {
|
||||
ExchangeType string `json:"exchangeType"`
|
||||
}
|
||||
|
||||
type GetUnBindReverseReq struct {
|
||||
ExchangeType string `json:"exchangeType" form:"exchangeType"`
|
||||
ApiId int `json:"apiId" form:"apiId" comment:"当前apiId"`
|
||||
}
|
||||
|
||||
// 未绑定反向下单查询返回结构体
|
||||
type UnBindReverseResp struct {
|
||||
Id int `json:"id"`
|
||||
UserId int64 `json:"userId"`
|
||||
Disabled bool `json:"disabled"`
|
||||
ApiName string `json:"apiName"`
|
||||
}
|
||||
|
||||
type GetReverseApiOptionsReq struct {
|
||||
Id int `json:"apiId" form:"id"`
|
||||
ApiId int `json:"apiId" form:"apiId"`
|
||||
}
|
||||
|
||||
type LineApiUserOptionResp struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
@ -1,162 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/common/dto"
|
||||
common "go-admin/common/models"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type LineReverseOrderGetPageReq struct {
|
||||
dto.Pagination `search:"-"`
|
||||
OrderSn string `form:"orderSn" search:"type:contains;column:order_sn;table:line_reverse_order" comment:"订单号"`
|
||||
OrderId string `form:"orderId" search:"type:contains;column:order_id;table:line_reverse_order" comment:"币安订单号"`
|
||||
FollowOrderSn string `form:"followOrderSn" search:"type:contains;column:follow_order_sn;table:line_reverse_order" comment:"跟随币安订单号"`
|
||||
OrderType int `form:"orderType" search:"type:exact;column:order_type;table:line_reverse_order" comment:"订单类型 0-主单 1-止损单 2-加仓 3-减仓"`
|
||||
PositionSide string `form:"positionSide" search:"type:exact;column:position_side;table:line_reverse_order" comment:"持仓方向 LONG-多 SHORT-空"`
|
||||
Side string `form:"side" search:"type:exact;column:side;table:line_reverse_order" comment:"买卖方向 SELL-卖 BUY-买"`
|
||||
Type string `form:"type" search:"type:exact;column:type;table:line_reverse_order" comment:"类型 LIMIT-限价 MARKET-市价 "`
|
||||
Category int `form:"category" search:"-" comment:"类型 0-主单 1-反单"`
|
||||
Status int `form:"status" search:"type:exact;column:status;table:line_reverse_order" comment:"状态 1-待下单 2-已下单 3-已成交 4-已平仓 5-已止损"`
|
||||
PositionId int `form:"positionId" search:"type:exact;column:position_id;table:line_reverse_order" comment:"持仓id"`
|
||||
LineReverseOrderOrder
|
||||
}
|
||||
|
||||
type LineReverseOrderOrder struct {
|
||||
Id string `form:"idOrder" search:"type:order;column:id;table:line_reverse_order"`
|
||||
PId string `form:"pIdOrder" search:"type:order;column:p_id;table:line_reverse_order"`
|
||||
OrderSn string `form:"orderSnOrder" search:"type:order;column:order_sn;table:line_reverse_order"`
|
||||
OrderId string `form:"orderIdOrder" search:"type:order;column:order_id;table:line_reverse_order"`
|
||||
FollowOrderSn string `form:"followOrderSnOrder" search:"type:order;column:follow_order_sn;table:line_reverse_order"`
|
||||
Symbol string `form:"symbolOrder" search:"type:order;column:symbol;table:line_reverse_order"`
|
||||
OrderType string `form:"orderTypeOrder" search:"type:order;column:order_type;table:line_reverse_order"`
|
||||
BuyPrice string `form:"buyPriceOrder" search:"type:order;column:buy_price;table:line_reverse_order"`
|
||||
Price string `form:"priceOrder" search:"type:order;column:price;table:line_reverse_order"`
|
||||
PriceU string `form:"priceUOrder" search:"type:order;column:price_u;table:line_reverse_order"`
|
||||
FinalPrice string `form:"finalPriceOrder" search:"type:order;column:final_price;table:line_reverse_order"`
|
||||
PositionSide string `form:"positionSideOrder" search:"type:order;column:position_side;table:line_reverse_order"`
|
||||
Side string `form:"sideOrder" search:"type:order;column:side;table:line_reverse_order"`
|
||||
SignPrice string `form:"signPriceOrder" search:"type:order;column:sign_price;table:line_reverse_order"`
|
||||
TriggerTime string `form:"triggerTimeOrder" search:"type:order;column:trigger_time;table:line_reverse_order"`
|
||||
Status string `form:"statusOrder" search:"type:order;column:status;table:line_reverse_order"`
|
||||
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:line_reverse_order"`
|
||||
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:line_reverse_order"`
|
||||
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:line_reverse_order"`
|
||||
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:line_reverse_order"`
|
||||
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:line_reverse_order"`
|
||||
}
|
||||
|
||||
func (m *LineReverseOrderGetPageReq) GetNeedSearch() interface{} {
|
||||
return *m
|
||||
}
|
||||
|
||||
type LineReverseOrderInsertReq struct {
|
||||
Id int `json:"-" comment:"主键id"` // 主键id
|
||||
PositionId int `json:"positionId" comment:"仓位id"`
|
||||
OrderSn string `json:"orderSn" comment:"订单号"`
|
||||
OrderId string `json:"orderId" comment:"币安订单号"`
|
||||
FollowOrderSn string `json:"followOrderSn" comment:"跟随币安订单号"`
|
||||
Symbol string `json:"symbol" comment:"交易对"`
|
||||
OrderType int `json:"orderType" comment:"订单类型 0-主单 1-止损单 2-加仓 3-减仓"`
|
||||
BuyPrice decimal.Decimal `json:"buyPrice" comment:"购买金额"`
|
||||
Price decimal.Decimal `json:"price" comment:"委托价格"`
|
||||
PriceU decimal.Decimal `json:"priceU" comment:"委托价格(U)"`
|
||||
FinalPrice decimal.Decimal `json:"finalPrice" comment:"实际成交价"`
|
||||
PositionSide string `json:"positionSide" comment:"持仓方向 LONG-多 SHORT-空"`
|
||||
Side string `json:"side" comment:"买卖方向 SELL-卖 BUY-买"`
|
||||
SignPrice decimal.Decimal `json:"signPrice" comment:"行情价"`
|
||||
TriggerTime time.Time `json:"triggerTime" comment:"触发时间"`
|
||||
Status int `json:"status" comment:"状态 1-待下单 2-已下单 3-已成交 4-已平仓 5-已止损"`
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
func (s *LineReverseOrderInsertReq) Generate(model *models.LineReverseOrder) {
|
||||
if s.Id == 0 {
|
||||
model.Model = common.Model{Id: s.Id}
|
||||
}
|
||||
model.OrderSn = s.OrderSn
|
||||
model.OrderId = s.OrderId
|
||||
model.FollowOrderSn = s.FollowOrderSn
|
||||
model.Symbol = s.Symbol
|
||||
model.OrderType = s.OrderType
|
||||
model.BuyPrice = s.BuyPrice
|
||||
model.Price = s.Price
|
||||
model.PriceU = s.PriceU
|
||||
model.FinalPrice = s.FinalPrice
|
||||
model.PositionSide = s.PositionSide
|
||||
model.Side = s.Side
|
||||
model.SignPrice = s.SignPrice
|
||||
model.TriggerTime = &s.TriggerTime
|
||||
model.Status = s.Status
|
||||
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
||||
}
|
||||
|
||||
func (s *LineReverseOrderInsertReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
type LineReverseOrderUpdateReq struct {
|
||||
Id int `uri:"id" comment:"主键id"` // 主键id
|
||||
PositionId int `json:"positionId" comment:"仓位id"`
|
||||
OrderSn string `json:"orderSn" comment:"订单号"`
|
||||
OrderId string `json:"orderId" comment:"币安订单号"`
|
||||
FollowOrderSn string `json:"followOrderSn" comment:"跟随币安订单号"`
|
||||
Symbol string `json:"symbol" comment:"交易对"`
|
||||
OrderType int `json:"orderType" comment:"订单类型 0-主单 1-止损单 2-加仓 3-减仓"`
|
||||
BuyPrice decimal.Decimal `json:"buyPrice" comment:"购买金额"`
|
||||
Price decimal.Decimal `json:"price" comment:"委托价格"`
|
||||
PriceU decimal.Decimal `json:"priceU" comment:"委托价格(U)"`
|
||||
FinalPrice decimal.Decimal `json:"finalPrice" comment:"实际成交价"`
|
||||
PositionSide string `json:"positionSide" comment:"持仓方向 LONG-多 SHORT-空"`
|
||||
Side string `json:"side" comment:"买卖方向 SELL-卖 BUY-买"`
|
||||
SignPrice decimal.Decimal `json:"signPrice" comment:"行情价"`
|
||||
TriggerTime time.Time `json:"triggerTime" comment:"触发时间"`
|
||||
Status int `json:"status" comment:"状态 1-待下单 2-已下单 3-已成交 4-已平仓 5-已止损"`
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
func (s *LineReverseOrderUpdateReq) Generate(model *models.LineReverseOrder) {
|
||||
if s.Id == 0 {
|
||||
model.Model = common.Model{Id: s.Id}
|
||||
}
|
||||
model.OrderSn = s.OrderSn
|
||||
model.OrderId = s.OrderId
|
||||
model.FollowOrderSn = s.FollowOrderSn
|
||||
model.Symbol = s.Symbol
|
||||
model.OrderType = s.OrderType
|
||||
model.BuyPrice = s.BuyPrice
|
||||
model.Price = s.Price
|
||||
model.PriceU = s.PriceU
|
||||
model.FinalPrice = s.FinalPrice
|
||||
model.PositionSide = s.PositionSide
|
||||
model.Side = s.Side
|
||||
model.SignPrice = s.SignPrice
|
||||
model.TriggerTime = &s.TriggerTime
|
||||
model.Status = s.Status
|
||||
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
||||
}
|
||||
|
||||
func (s *LineReverseOrderUpdateReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
// LineReverseOrderGetReq 功能获取请求参数
|
||||
type LineReverseOrderGetReq struct {
|
||||
Id int `uri:"id"`
|
||||
}
|
||||
|
||||
func (s *LineReverseOrderGetReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
// LineReverseOrderDeleteReq 功能删除请求参数
|
||||
type LineReverseOrderDeleteReq struct {
|
||||
Ids []int `json:"ids"`
|
||||
}
|
||||
|
||||
func (s *LineReverseOrderDeleteReq) GetId() interface{} {
|
||||
return s.Ids
|
||||
}
|
||||
@ -1,155 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/common/dto"
|
||||
common "go-admin/common/models"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type LineReversePositionGetPageReq struct {
|
||||
dto.Pagination `search:"-"`
|
||||
ApiId int64 `form:"apiId" search:"type:exact;column:api_id;table:line_reverse_position" comment:"api_id"`
|
||||
ReverseApiId int64 `form:"reverseApiId" search:"type:exact;column:reverse_api_id;table:line_reverse_position" comment:"反单api_id"`
|
||||
Side string `form:"side" search:"type:exact;column:side;table:line_reverse_position" comment:"买卖方向 BUY SELL"`
|
||||
PositionSide string `form:"positionSide" search:"type:exact;column:position_side;table:line_reverse_position" comment:"持仓方向 LONG SHORT"`
|
||||
Symbol string `form:"symbol" search:"type:contains;column:symbol;table:line_reverse_position" comment:"交易对"`
|
||||
LineReversePositionOrder
|
||||
}
|
||||
|
||||
type LineReversePositionOrder struct {
|
||||
Id string `form:"idOrder" search:"type:order;column:id;table:line_reverse_position"`
|
||||
ApiId string `form:"apiIdOrder" search:"type:order;column:api_id;table:line_reverse_position"`
|
||||
ReverseApiId string `form:"reverseApiIdOrder" search:"type:order;column:reverse_api_id;table:line_reverse_position"`
|
||||
ReverseAmount string `form:"reverseAmountOrder" search:"type:order;column:reverse_amount;table:line_reverse_position"`
|
||||
Side string `form:"sideOrder" search:"type:order;column:side;table:line_reverse_position"`
|
||||
PositionSide string `form:"positionSideOrder" search:"type:order;column:position_side;table:line_reverse_position"`
|
||||
Symbol string `form:"symbolOrder" search:"type:order;column:symbol;table:line_reverse_position"`
|
||||
Status string `form:"statusOrder" search:"type:order;column:status;table:line_reverse_position"`
|
||||
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:line_reverse_position"`
|
||||
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:line_reverse_position"`
|
||||
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:line_reverse_position"`
|
||||
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:line_reverse_position"`
|
||||
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:line_reverse_position"`
|
||||
}
|
||||
|
||||
func (m *LineReversePositionGetPageReq) GetNeedSearch() interface{} {
|
||||
return *m
|
||||
}
|
||||
|
||||
type LineReversePositionInsertReq struct {
|
||||
Id int `json:"-" comment:"主键"` // 主键
|
||||
ApiId int `json:"apiId" comment:"api_id"`
|
||||
ReverseApiId int `json:"reverseApiId" comment:"反单api_id"`
|
||||
ReverseAmount decimal.Decimal `json:"reverseAmount" comment:"反单仓位"`
|
||||
Side string `json:"side" comment:"买卖方向 BUY SELL"`
|
||||
PositionSide string `json:"positionSide" comment:"持仓方向 LONG SHORT"`
|
||||
Symbol string `json:"symbol" comment:"交易对"`
|
||||
Status int `json:"status" comment:"仓位状态 1-已开仓 2-已平仓"`
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
func (s *LineReversePositionInsertReq) Generate(model *models.LineReversePosition) {
|
||||
if s.Id == 0 {
|
||||
model.Model = common.Model{Id: s.Id}
|
||||
}
|
||||
model.ApiId = s.ApiId
|
||||
model.ReverseApiId = s.ReverseApiId
|
||||
model.ReverseAmount = s.ReverseAmount
|
||||
model.Side = s.Side
|
||||
model.PositionSide = s.PositionSide
|
||||
model.Symbol = s.Symbol
|
||||
model.Status = s.Status
|
||||
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
||||
}
|
||||
|
||||
func (s *LineReversePositionInsertReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
type LineReversePositionUpdateReq struct {
|
||||
Id int `uri:"id" comment:"主键"` // 主键
|
||||
ApiId int `json:"apiId" comment:"api_id"`
|
||||
ReverseApiId int `json:"reverseApiId" comment:"反单api_id"`
|
||||
ReverseAmount decimal.Decimal `json:"reverseAmount" comment:"反单仓位"`
|
||||
Side string `json:"side" comment:"买卖方向 BUY SELL"`
|
||||
PositionSide string `json:"positionSide" comment:"持仓方向 LONG SHORT"`
|
||||
Symbol string `json:"symbol" comment:"交易对"`
|
||||
Status int `json:"status" comment:"仓位状态 1-已开仓 2-已平仓"`
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
func (s *LineReversePositionUpdateReq) Generate(model *models.LineReversePosition) {
|
||||
if s.Id == 0 {
|
||||
model.Model = common.Model{Id: s.Id}
|
||||
}
|
||||
model.ApiId = s.ApiId
|
||||
model.ReverseApiId = s.ReverseApiId
|
||||
model.ReverseAmount = s.ReverseAmount
|
||||
model.Side = s.Side
|
||||
model.PositionSide = s.PositionSide
|
||||
model.Symbol = s.Symbol
|
||||
model.Status = s.Status
|
||||
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
||||
}
|
||||
|
||||
func (s *LineReversePositionUpdateReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
// LineReversePositionGetReq 功能获取请求参数
|
||||
type LineReversePositionGetReq struct {
|
||||
Id int `uri:"id"`
|
||||
}
|
||||
|
||||
func (s *LineReversePositionGetReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
// LineReversePositionDeleteReq 功能删除请求参数
|
||||
type LineReversePositionDeleteReq struct {
|
||||
Ids []int `json:"ids"`
|
||||
}
|
||||
|
||||
func (s *LineReversePositionDeleteReq) GetId() interface{} {
|
||||
return s.Ids
|
||||
}
|
||||
|
||||
type LineReversePositionListResp struct {
|
||||
Id int `json:"id"`
|
||||
ApiName string `json:"apiName"`
|
||||
ReverseApiName string `json:"reverseApiName"`
|
||||
Amount decimal.Decimal `json:"amount"`
|
||||
TotalAmount decimal.Decimal `json:"totalAmount"`
|
||||
ReverseAmount decimal.Decimal `json:"reverseAmount"`
|
||||
TotalReverseAmount decimal.Decimal `json:"totalReverseAmount"`
|
||||
Side string `json:"side"`
|
||||
PositionSide string `json:"positionSide"`
|
||||
Symbol string `json:"symbol"`
|
||||
Status int `json:"status"`
|
||||
ReverseStatus int `json:"reverseStatus"`
|
||||
AveragePrice decimal.Decimal `json:"averagePrice"`
|
||||
ReverseAveragePrice decimal.Decimal `json:"reverseAveragePrice"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
|
||||
type LineReversePositionCloseReq struct {
|
||||
PositionId int `uri:"id" form:"id" comment:"仓位id"`
|
||||
}
|
||||
|
||||
type LineReversePositionCloseBatchReq struct {
|
||||
PositionSide string `json:"positionSide"`
|
||||
Symbol string `json:"symbol"`
|
||||
ReverseApiIds []int `json:"reverseApiIds" form:"reverseApiIds" comment:"反单api_id"`
|
||||
}
|
||||
|
||||
type GetPositionSymbolReq struct {
|
||||
ReverseApiId int `form:"reverseApiId" comment:"反单api_id"`
|
||||
PositionSide string `form:"positionSide" comment:"持仓方向 LONG SHORT"`
|
||||
}
|
||||
|
||||
type PositionSymbolResp struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/common/dto"
|
||||
common "go-admin/common/models"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type LineReverseSettingGetPageReq struct {
|
||||
dto.Pagination `search:"-"`
|
||||
LineReverseSettingOrder
|
||||
}
|
||||
|
||||
type LineReverseSettingOrder struct {
|
||||
Id string `form:"idOrder" search:"type:order;column:id;table:line_reverse_setting"`
|
||||
ReverseOrderType string `form:"reverseOrderTypeOrder" search:"type:order;column:reverse_order_type;table:line_reverse_setting"`
|
||||
ReversePremiumRatio string `form:"reversePremiumRatioOrder" search:"type:order;column:reverse_premium_ratio;table:line_reverse_setting"`
|
||||
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:line_reverse_setting"`
|
||||
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:line_reverse_setting"`
|
||||
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:line_reverse_setting"`
|
||||
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:line_reverse_setting"`
|
||||
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:line_reverse_setting"`
|
||||
}
|
||||
|
||||
func (m *LineReverseSettingGetPageReq) GetNeedSearch() interface{} {
|
||||
return *m
|
||||
}
|
||||
|
||||
type LineReverseSettingInsertReq struct {
|
||||
Id int `json:"-" comment:"主键id"` // 主键id
|
||||
ReverseOrderType string `json:"reverseOrderType" comment:"反单下单类型 LIMIT-限价 MARKET-市价"`
|
||||
ReversePremiumRatio decimal.Decimal `json:"reversePremiumRatio" comment:"溢价百分比"`
|
||||
TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"`
|
||||
StopLossRatio decimal.Decimal `json:"stopLossRatio" comment:"止损百分比"`
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
func (s *LineReverseSettingInsertReq) Generate(model *models.LineReverseSetting) {
|
||||
if s.Id == 0 {
|
||||
model.Model = common.Model{Id: s.Id}
|
||||
}
|
||||
model.ReverseOrderType = s.ReverseOrderType
|
||||
model.ReversePremiumRatio = s.ReversePremiumRatio
|
||||
model.StopLossRatio = s.StopLossRatio
|
||||
model.TakeProfitRatio = s.TakeProfitRatio
|
||||
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
||||
}
|
||||
|
||||
func (s *LineReverseSettingInsertReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
type LineReverseSettingUpdateReq struct {
|
||||
Id int `uri:"id" comment:"主键id"` // 主键id
|
||||
ReverseOrderType string `json:"reverseOrderType" comment:"反单下单类型 LIMIT-限价 MARKET-市价"`
|
||||
ReversePremiumRatio decimal.Decimal `json:"reversePremiumRatio" comment:"溢价百分比"`
|
||||
TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"`
|
||||
StopLossRatio decimal.Decimal `json:"stopLossRatio" comment:"止损百分比"`
|
||||
common.ControlBy
|
||||
}
|
||||
|
||||
func (s *LineReverseSettingUpdateReq) Generate(model *models.LineReverseSetting) {
|
||||
if s.Id == 0 {
|
||||
model.Model = common.Model{Id: s.Id}
|
||||
}
|
||||
model.ReverseOrderType = s.ReverseOrderType
|
||||
model.ReversePremiumRatio = s.ReversePremiumRatio
|
||||
model.StopLossRatio = s.StopLossRatio
|
||||
model.TakeProfitRatio = s.TakeProfitRatio
|
||||
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
|
||||
}
|
||||
|
||||
func (s *LineReverseSettingUpdateReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
// LineReverseSettingGetReq 功能获取请求参数
|
||||
type LineReverseSettingGetReq struct {
|
||||
Id int `uri:"id"`
|
||||
}
|
||||
|
||||
func (s *LineReverseSettingGetReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
// LineReverseSettingDeleteReq 功能删除请求参数
|
||||
type LineReverseSettingDeleteReq struct {
|
||||
Ids []int `json:"ids"`
|
||||
}
|
||||
|
||||
func (s *LineReverseSettingDeleteReq) GetId() interface{} {
|
||||
return s.Ids
|
||||
}
|
||||
@ -8,7 +8,6 @@ import (
|
||||
"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/go-redis/redis/v8"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
@ -26,43 +25,6 @@ type LineApiUser struct {
|
||||
service.Service
|
||||
}
|
||||
|
||||
// 获取所有启用的反单api用户
|
||||
func (e LineApiUser) GetReverseApiOptionsAll(user *[]dto.LineApiUserOptionResp) error {
|
||||
var data models.LineApiUser
|
||||
var datas []models.LineApiUser
|
||||
|
||||
if err := e.Orm.Model(&data).Where("open_status = 1 AND subordinate = '2'").Find(&datas).Error; err != nil {
|
||||
e.Log.Errorf("LineApiUserService GetReverseApiOptionsAll error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for _, item := range datas {
|
||||
*user = append(*user, dto.LineApiUserOptionResp{
|
||||
Id: item.Id,
|
||||
Name: item.ApiName,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取可以绑定的api列表
|
||||
func (e LineApiUser) GetReverseApiOptions(req *dto.GetReverseApiOptionsReq, user *[]models.LineApiUser) error {
|
||||
query := e.Orm.Model(models.LineApiUser{}).
|
||||
Where("subordinate ='0' and id != ?", req.Id)
|
||||
|
||||
if req.ApiId > 0 {
|
||||
query = query.Or(" id =?", req.ApiId)
|
||||
}
|
||||
|
||||
if err := query.Find(user).Error; err != nil {
|
||||
e.Log.Errorf("LineApiUserService GetReverseApiOptions error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPage 获取LineApiUser列表
|
||||
func (e *LineApiUser) GetPage(c *dto.LineApiUserGetPageReq, p *actions.DataPermission, list *[]models.LineApiUser, count *int64) error {
|
||||
var err error
|
||||
@ -90,19 +52,19 @@ func (e *LineApiUser) GetPage(c *dto.LineApiUserGetPageReq, p *actions.DataPermi
|
||||
return err
|
||||
}
|
||||
|
||||
var userSub binancedto.UserSubscribeState
|
||||
for index := range *list {
|
||||
val, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.USER_SUBSCRIBE, (*list)[index].ApiKey))
|
||||
var userSub binancedto.UserSubscribeState
|
||||
|
||||
if val != "" {
|
||||
sonic.Unmarshal([]byte(val), &userSub)
|
||||
|
||||
if userSub.FuturesLastTime != nil {
|
||||
(*list)[index].FuturesLastTime = userSub.FuturesLastTime.Format("2006-01-02 15:04:05")
|
||||
(*list)[index].FuturesLastTime = userSub.FuturesLastTime
|
||||
}
|
||||
|
||||
if userSub.SpotLastTime != nil {
|
||||
(*list)[index].SpotLastTime = userSub.SpotLastTime.Format("2006-01-02 15:04:05")
|
||||
(*list)[index].SpotLastTime = userSub.SpotLastTime
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -135,37 +97,13 @@ func (e *LineApiUser) Insert(c *dto.LineApiUserInsertReq) error {
|
||||
var err error
|
||||
var data models.LineApiUser
|
||||
c.Generate(&data)
|
||||
err = e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||
if c.ReverseApiId > 0 {
|
||||
var count int64
|
||||
if err2 := tx.Model(models.LineApiUser{}).Where("subordinate <> '0' AND id=?", c.ReverseApiId).Count(&count).Error; err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("反向api已被使用")
|
||||
}
|
||||
|
||||
if err2 := tx.Model(models.LineApiUser{}).Where("id =?", c.ReverseApiId).Update("subordinate", '2').Error; err2 != nil {
|
||||
return err2
|
||||
}
|
||||
data.Subordinate = "1"
|
||||
}
|
||||
|
||||
if err2 := tx.Create(&data).Error; err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
err = e.Orm.Create(&data).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("LineApiUserService Insert error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err2 := e.CacheRelation(); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
e.saveCache(data)
|
||||
val, _ := sonic.MarshalString(&data)
|
||||
|
||||
if val != "" {
|
||||
@ -193,7 +131,7 @@ func (e *LineApiUser) restartWebsocket(data models.LineApiUser) {
|
||||
fuSocket.Stop()
|
||||
}
|
||||
|
||||
e.CacheRelation()
|
||||
e.saveCache(data)
|
||||
|
||||
OpenUserBinanceWebsocket(data)
|
||||
}
|
||||
@ -211,23 +149,6 @@ func (e *LineApiUser) saveCache(data models.LineApiUser) {
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存关系
|
||||
// cacheAll 是否缓存所有关系
|
||||
func (e *LineApiUser) CacheRelation() error {
|
||||
var datas *[]models.LineApiUser
|
||||
|
||||
if err := e.Orm.Model(&models.LineApiUser{}).Where("open_status = 1").Find(&datas).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, data := range *datas {
|
||||
// cacheStrs = append(cacheStrs, fmt.Sprintf(rediskey.API_USER, data.Id))
|
||||
e.saveCache(data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
打开用户websocket订阅
|
||||
*/
|
||||
@ -276,7 +197,6 @@ func (e *LineApiUser) Update(c *dto.LineApiUserUpdateReq, p *actions.DataPermiss
|
||||
actions.Permission(data.TableName(), p),
|
||||
).First(&data, c.GetId())
|
||||
oldApiKey := data.ApiKey
|
||||
oldReverseApiId := data.ReverseApiId
|
||||
|
||||
c.Generate(&data)
|
||||
|
||||
@ -284,33 +204,6 @@ func (e *LineApiUser) Update(c *dto.LineApiUserUpdateReq, p *actions.DataPermiss
|
||||
|
||||
//事务
|
||||
err = e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||
if data.ReverseApiId > 0 && data.ReverseApiId != oldReverseApiId {
|
||||
var count int64
|
||||
if err2 := tx.Model(models.LineApiUser{}).Where("subordinate <> '0' AND id=?", c.ReverseApiId).Count(&count).Error; err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("反向api已被使用")
|
||||
}
|
||||
|
||||
if err2 := tx.Model(models.LineApiUser{}).Where("id =?", c.ReverseApiId).Update("subordinate", "2").Error; err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
data.Subordinate = "1"
|
||||
}
|
||||
|
||||
if oldReverseApiId > 0 && oldReverseApiId != data.ReverseApiId {
|
||||
if err2 := tx.Model(models.LineApiUser{}).Where("id =?", oldReverseApiId).Update("subordinate", "0").Error; err2 != nil {
|
||||
e.Log.Errorf("解绑反向api失败 api:%d err:%v", oldReverseApiId, err2)
|
||||
return fmt.Errorf("解绑反向api失败")
|
||||
}
|
||||
|
||||
if data.ReverseApiId == 0 {
|
||||
data.Subordinate = "0"
|
||||
}
|
||||
}
|
||||
|
||||
db := tx.Save(&data)
|
||||
if err = db.Error; err != nil {
|
||||
e.Log.Errorf("LineApiUserService Save error:%s \r\n", err)
|
||||
@ -333,9 +226,7 @@ func (e *LineApiUser) Update(c *dto.LineApiUserUpdateReq, p *actions.DataPermiss
|
||||
return err
|
||||
}
|
||||
|
||||
if err2 := e.CacheRelation(); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
e.saveCache(data)
|
||||
|
||||
//旧key和新的key不一样,则关闭旧的websocket
|
||||
if oldApiKey != data.ApiKey {
|
||||
@ -398,14 +289,10 @@ func (e *LineApiUser) Remove(d *dto.LineApiUserDeleteReq, p *actions.DataPermiss
|
||||
|
||||
_, err := helper.DefaultRedis.BatchDeleteKeys(delKeys)
|
||||
|
||||
if err != nil && !errors.Is(err, redis.Nil) {
|
||||
if err != nil {
|
||||
e.Log.Error("批量删除api_user key失败", err)
|
||||
}
|
||||
|
||||
if err2 := e.CacheRelation(); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -527,44 +414,3 @@ func (e *LineApiUser) GetActiveApis(apiIds []int) ([]int, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetUnBindApiUser 获取未绑定反向下单的的api用户
|
||||
func (e *LineApiUser) GetUnBindApiUser(req *dto.GetUnBindReverseReq, list *[]dto.UnBindReverseResp) error {
|
||||
var datas []models.LineApiUser
|
||||
var data models.LineApiUser
|
||||
var count int64
|
||||
|
||||
if req.ApiId > 0 {
|
||||
e.Orm.Model(data).Where("id =? AND subordinate = '2'", req.ApiId).Count(&count)
|
||||
|
||||
if count > 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
query := e.Orm.Model(&data).
|
||||
Where("open_status =1 AND exchange_type = ? ", req.ExchangeType)
|
||||
|
||||
if err := query.
|
||||
Find(&datas).Error; err != nil {
|
||||
e.Log.Error("获取未绑定用户失败:", err)
|
||||
return fmt.Errorf("获取未绑定用户失败")
|
||||
}
|
||||
|
||||
for _, item := range datas {
|
||||
if item.Id == req.ApiId {
|
||||
continue
|
||||
}
|
||||
|
||||
listItem := dto.UnBindReverseResp{
|
||||
Id: item.Id,
|
||||
UserId: item.UserId,
|
||||
ApiName: item.ApiName,
|
||||
Disabled: item.Subordinate != "0",
|
||||
}
|
||||
|
||||
(*list) = append((*list), listItem)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1267,14 +1267,25 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p
|
||||
var symbolGroupInfo models.LineSymbolGroup
|
||||
e.Orm.Model(&models.LineSymbolGroup{}).Where("id = ?", utility.StringToInt(batchReq.SymbolGroupId)).Find(&symbolGroupInfo)
|
||||
if symbolGroupInfo.Id <= 0 || symbolGroupInfo.Symbol == "" {
|
||||
*errs = append(*errs, errors.New(fmt.Sprintf("选择的交易对组:%s不存在或交易对组的交易对为空", batchReq.SymbolGroupId)))
|
||||
*errs = append(*errs, fmt.Errorf("选择的交易对组:%s不存在或交易对组的交易对为空", batchReq.SymbolGroupId))
|
||||
return nil
|
||||
}
|
||||
|
||||
if batchReq.StrategyTemplateType == 1 && batchReq.StrategyTemplateId > 0 {
|
||||
cachePriceSymbols, _ := helper.DefaultRedis.GetAllList(rediskey.CacheSymbolLastPrice)
|
||||
symbols := strings.Split(symbolGroupInfo.Symbol, ",")
|
||||
|
||||
for _, symbol := range symbols {
|
||||
if !utility.ContainsStr(cachePriceSymbols, symbol) {
|
||||
*errs = append(*errs, fmt.Errorf("交易对[%s]涨跌幅缓存不存在", symbol))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
batchReq.Symbol = symbolGroupInfo.Symbol
|
||||
}
|
||||
|
||||
//脚本次数
|
||||
// if batchReq.OrderNum > 0 {
|
||||
apiUserIds := strings.Split(batchReq.ApiUserId, ",")
|
||||
var tickerSymbol string
|
||||
if batchReq.SymbolType == global.SYMBOL_SPOT {
|
||||
@ -1299,7 +1310,7 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p
|
||||
log.ScriptParams = string(marshal)
|
||||
log.AdminId = 0
|
||||
log.Status = "0"
|
||||
//scriptLogs = append(scriptLogs, log)
|
||||
|
||||
err := e.Orm.Model(&models.LinePreScript{}).Create(&log).Error
|
||||
if err != nil {
|
||||
*errs = append(*errs, fmt.Errorf("记录脚本失败:%+v", err.Error()))
|
||||
@ -1339,6 +1350,8 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p
|
||||
req.ReduceStopLossRatio = batchReq.ReduceStopLossRatio
|
||||
req.ReduceTakeProfitRatio = batchReq.ReduceTakeProfitRatio
|
||||
req.CreateBy = batchReq.CreateBy
|
||||
req.StrategyTemplateId = batchReq.StrategyTemplateId
|
||||
req.StrategyTemplateType = batchReq.StrategyTemplateType
|
||||
|
||||
e.AddPreOrderCheck(&req, p, errs, tickerSymbol)
|
||||
}
|
||||
|
||||
@ -1,114 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/actions"
|
||||
cDto "go-admin/common/dto"
|
||||
)
|
||||
|
||||
type LineReverseOrder struct {
|
||||
service.Service
|
||||
}
|
||||
|
||||
// GetPage 获取LineReverseOrder列表
|
||||
func (e *LineReverseOrder) GetPage(c *dto.LineReverseOrderGetPageReq, p *actions.DataPermission, list *[]models.LineReverseOrder, count *int64) error {
|
||||
var err error
|
||||
var data models.LineReverseOrder
|
||||
query := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
cDto.MakeCondition(c.GetNeedSearch()),
|
||||
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||
actions.Permission(data.TableName(), p),
|
||||
)
|
||||
|
||||
if c.Category >= 0 {
|
||||
query = query.Where("category =?", c.Category)
|
||||
}
|
||||
|
||||
err = query.
|
||||
Find(list).Limit(-1).Offset(-1).
|
||||
Count(count).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("LineReverseOrderService GetPage error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取LineReverseOrder对象
|
||||
func (e *LineReverseOrder) Get(d *dto.LineReverseOrderGetReq, p *actions.DataPermission, model *models.LineReverseOrder) error {
|
||||
var data models.LineReverseOrder
|
||||
|
||||
err := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).
|
||||
First(model, d.GetId()).Error
|
||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = errors.New("查看对象不存在或无权查看")
|
||||
e.Log.Errorf("Service GetLineReverseOrder error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
e.Log.Errorf("db error:%s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert 创建LineReverseOrder对象
|
||||
func (e *LineReverseOrder) Insert(c *dto.LineReverseOrderInsertReq) error {
|
||||
var err error
|
||||
var data models.LineReverseOrder
|
||||
c.Generate(&data)
|
||||
err = e.Orm.Create(&data).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("LineReverseOrderService Insert error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update 修改LineReverseOrder对象
|
||||
func (e *LineReverseOrder) Update(c *dto.LineReverseOrderUpdateReq, p *actions.DataPermission) error {
|
||||
var err error
|
||||
var data = models.LineReverseOrder{}
|
||||
e.Orm.Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).First(&data, c.GetId())
|
||||
c.Generate(&data)
|
||||
|
||||
db := e.Orm.Save(&data)
|
||||
if err = db.Error; err != nil {
|
||||
e.Log.Errorf("LineReverseOrderService Save error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权更新该数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove 删除LineReverseOrder
|
||||
func (e *LineReverseOrder) Remove(d *dto.LineReverseOrderDeleteReq, p *actions.DataPermission) error {
|
||||
var data models.LineReverseOrder
|
||||
|
||||
db := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).Delete(&data, d.GetId())
|
||||
if err := db.Error; err != nil {
|
||||
e.Log.Errorf("Service RemoveLineReverseOrder error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权删除该数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -1,324 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/actions"
|
||||
cDto "go-admin/common/dto"
|
||||
"go-admin/common/global"
|
||||
"go-admin/pkg/utility"
|
||||
"go-admin/pkg/utility/snowflakehelper"
|
||||
"go-admin/services/binanceservice"
|
||||
"go-admin/services/cacheservice"
|
||||
)
|
||||
|
||||
type LineReversePosition struct {
|
||||
service.Service
|
||||
}
|
||||
|
||||
// 批量关闭仓位
|
||||
func (e LineReversePosition) ClosePositionBatch(req *dto.LineReversePositionCloseBatchReq, p *actions.DataPermission, userId int, errs *[]string) error {
|
||||
var positions []models.LineReversePosition
|
||||
var entity models.LineReversePosition
|
||||
query := e.Orm.Model(&entity).
|
||||
Scopes(
|
||||
actions.Permission(entity.TableName(), p),
|
||||
).
|
||||
Where("reverse_status = 1 and position_side =?", req.PositionSide)
|
||||
|
||||
if len(req.Symbol) > 0 {
|
||||
query = query.Where("symbol =?", req.Symbol)
|
||||
}
|
||||
|
||||
if len(req.ReverseApiIds) > 0 {
|
||||
query = query.Where("reverse_api_id in (?)", req.ReverseApiIds)
|
||||
}
|
||||
|
||||
if err := query.Find(&positions).Error; err != nil {
|
||||
e.Log.Errorf("LineReversePositionService ClosePositionBatch error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(positions) == 0 {
|
||||
return errors.New("没有需要关闭的仓位")
|
||||
}
|
||||
|
||||
for _, position := range positions {
|
||||
if err1 := e.Close(position); err1 != nil {
|
||||
*errs = append(*errs, err1.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClosePosition 关闭单个仓位
|
||||
func (e LineReversePosition) ClosePosition(req *dto.LineReversePositionCloseReq, p *actions.DataPermission, userId int) error {
|
||||
var data models.LineReversePosition
|
||||
err := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).
|
||||
Where("id =?", req.PositionId).First(&data).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("LineReversePositionService ClosePosition error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = e.Close(data)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *LineReversePosition) Close(data models.LineReversePosition) error {
|
||||
if data.ReverseStatus != 1 {
|
||||
return fmt.Errorf("%s-%s 仓位无法关闭", data.Symbol, data.PositionSide)
|
||||
}
|
||||
|
||||
apiInfo, err := binanceservice.GetApiInfo(data.ReverseApiId)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("api %d不存在", data.ReverseApiId)
|
||||
}
|
||||
futApi := binanceservice.FutRestApi{}
|
||||
futApiV2 := binanceservice.FuturesResetV2{}
|
||||
holdData := binanceservice.HoldeData{}
|
||||
err = futApi.GetPositionData(&apiInfo, data.Symbol, data.PositionSide, &holdData)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取币安持仓失败 %v", err)
|
||||
}
|
||||
setting, err := cacheservice.GetReverseSetting(e.Orm)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取反单设置失败")
|
||||
}
|
||||
|
||||
symbol, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, data.Symbol, 0)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取%s的交易对信息失败", data.Symbol)
|
||||
}
|
||||
|
||||
lastPrice, err := decimal.NewFromString(symbol.LastPrice)
|
||||
if err != nil {
|
||||
return fmt.Errorf("最新价格失败,%v", lastPrice)
|
||||
}
|
||||
side := ""
|
||||
var price decimal.Decimal
|
||||
|
||||
if data.PositionSide == "LONG" {
|
||||
side = "SELL"
|
||||
price = decimal.NewFromInt(100).Sub(setting.ReversePremiumRatio).Div(decimal.NewFromInt(100)).Mul(lastPrice).Round(int32(symbol.PriceDigit))
|
||||
} else {
|
||||
side = "BUY"
|
||||
price = decimal.NewFromInt(100).Add(setting.ReversePremiumRatio).Div(decimal.NewFromInt(100)).Mul(lastPrice).Round(int32(symbol.PriceDigit))
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
order := models.LineReverseOrder{
|
||||
OrderSn: snowflakehelper.GetOrderNo(),
|
||||
PositionId: data.Id,
|
||||
PositionSide: data.PositionSide,
|
||||
Symbol: data.Symbol,
|
||||
TotalNum: holdData.TotalQuantity,
|
||||
Category: 1,
|
||||
OrderType: 4,
|
||||
Side: side,
|
||||
Price: price,
|
||||
SignPrice: lastPrice,
|
||||
Type: "LIMIT",
|
||||
TriggerTime: &now,
|
||||
Status: 1,
|
||||
}
|
||||
|
||||
if holdData.TotalQuantity.Cmp(decimal.Zero) > 0 {
|
||||
if err := e.Orm.Create(&order).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
params := binanceservice.FutOrderPlace{
|
||||
ApiId: data.ReverseApiId,
|
||||
Symbol: data.Symbol,
|
||||
Side: order.Side,
|
||||
PositionSide: order.PositionSide,
|
||||
Quantity: order.TotalNum,
|
||||
Price: order.Price,
|
||||
SideType: order.Type,
|
||||
OpenOrder: 0,
|
||||
OrderType: "LIMIT",
|
||||
NewClientOrderId: order.OrderSn,
|
||||
}
|
||||
|
||||
if err := futApiV2.OrderPlaceLoop(&apiInfo, params); err != nil {
|
||||
e.Log.Errorf("币安下单失败 symbol:%s custom:%s :%v", params.Symbol, order.OrderSn, err)
|
||||
if err1 := e.Orm.Model(&order).Where("status = 1").Updates(map[string]interface{}{"status": 8, "remark": err.Error(), "updated_at": time.Now()}).Error; err1 != nil {
|
||||
e.Log.Errorf("更新订单状态失败 symbol:%s custom:%s :%v", params.Symbol, order.OrderSn, err1)
|
||||
return err1
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
order.Status = 8
|
||||
order.Remark = "已经没有持仓"
|
||||
|
||||
err = e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||
if err1 := tx.Create(&order).Error; err1 != nil {
|
||||
return err1
|
||||
}
|
||||
|
||||
if err1 := tx.Model(&data).Where("reverse_status =1").Updates(map[string]interface{}{"reverse_status": 2, "updated_at": time.Now(), "reverse_amount": 0}).Error; err1 != nil {
|
||||
return err1
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
e.Log.Errorf("修改失败 %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPage 获取LineReversePosition列表
|
||||
func (e *LineReversePosition) GetPage(c *dto.LineReversePositionGetPageReq, p *actions.DataPermission, list *[]dto.LineReversePositionListResp, count *int64) error {
|
||||
var err error
|
||||
var data models.LineReversePosition
|
||||
var datas []models.LineReversePosition
|
||||
|
||||
err = e.Orm.Model(&data).
|
||||
Scopes(
|
||||
cDto.MakeCondition(c.GetNeedSearch()),
|
||||
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||
actions.Permission(data.TableName(), p),
|
||||
).
|
||||
Find(&datas).Limit(-1).Offset(-1).
|
||||
Count(count).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("LineReversePositionService GetPage error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
userIds := make([]int, 0)
|
||||
|
||||
for _, item := range datas {
|
||||
if !utility.ContainsInt(userIds, item.ApiId) {
|
||||
userIds = append(userIds, item.ApiId)
|
||||
}
|
||||
|
||||
if !utility.ContainsInt(userIds, item.ReverseApiId) {
|
||||
userIds = append(userIds, item.ReverseApiId)
|
||||
}
|
||||
}
|
||||
|
||||
userMap := make(map[int]string, 0)
|
||||
|
||||
if len(userIds) > 0 {
|
||||
var users []models.LineApiUser
|
||||
e.Orm.Model(&models.LineApiUser{}).
|
||||
Where("id IN (?)", userIds).
|
||||
Find(&users)
|
||||
|
||||
for _, item := range users {
|
||||
userMap[int(item.Id)] = item.ApiName
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range datas {
|
||||
var resp dto.LineReversePositionListResp
|
||||
copier.Copy(&resp, &item)
|
||||
|
||||
if userName, ok := userMap[item.ApiId]; ok {
|
||||
resp.ApiName = userName
|
||||
}
|
||||
|
||||
if userName, ok := userMap[item.ReverseApiId]; ok {
|
||||
resp.ReverseApiName = userName
|
||||
}
|
||||
|
||||
*list = append(*list, resp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取LineReversePosition对象
|
||||
func (e *LineReversePosition) Get(d *dto.LineReversePositionGetReq, p *actions.DataPermission, model *models.LineReversePosition) error {
|
||||
var data models.LineReversePosition
|
||||
|
||||
err := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).
|
||||
First(model, d.GetId()).Error
|
||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = errors.New("查看对象不存在或无权查看")
|
||||
e.Log.Errorf("Service GetLineReversePosition error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
e.Log.Errorf("db error:%s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert 创建LineReversePosition对象
|
||||
func (e *LineReversePosition) Insert(c *dto.LineReversePositionInsertReq) error {
|
||||
var err error
|
||||
var data models.LineReversePosition
|
||||
c.Generate(&data)
|
||||
err = e.Orm.Create(&data).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("LineReversePositionService Insert error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update 修改LineReversePosition对象
|
||||
func (e *LineReversePosition) Update(c *dto.LineReversePositionUpdateReq, p *actions.DataPermission) error {
|
||||
var err error
|
||||
var data = models.LineReversePosition{}
|
||||
e.Orm.Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).First(&data, c.GetId())
|
||||
c.Generate(&data)
|
||||
|
||||
db := e.Orm.Save(&data)
|
||||
if err = db.Error; err != nil {
|
||||
e.Log.Errorf("LineReversePositionService Save error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权更新该数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove 删除LineReversePosition
|
||||
func (e *LineReversePosition) Remove(d *dto.LineReversePositionDeleteReq, p *actions.DataPermission) error {
|
||||
var data models.LineReversePosition
|
||||
|
||||
db := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).Delete(&data, d.GetId())
|
||||
if err := db.Error; err != nil {
|
||||
e.Log.Errorf("Service RemoveLineReversePosition error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权删除该数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -1,136 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common/actions"
|
||||
"go-admin/common/const/rediskey"
|
||||
cDto "go-admin/common/dto"
|
||||
"go-admin/common/helper"
|
||||
)
|
||||
|
||||
type LineReverseSetting struct {
|
||||
service.Service
|
||||
}
|
||||
|
||||
// GetPage 获取LineReverseSetting列表
|
||||
func (e *LineReverseSetting) GetPage(c *dto.LineReverseSettingGetPageReq, p *actions.DataPermission, list *[]models.LineReverseSetting, count *int64) error {
|
||||
var err error
|
||||
var data models.LineReverseSetting
|
||||
|
||||
err = e.Orm.Model(&data).
|
||||
Scopes(
|
||||
cDto.MakeCondition(c.GetNeedSearch()),
|
||||
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
|
||||
actions.Permission(data.TableName(), p),
|
||||
).
|
||||
Find(list).Limit(-1).Offset(-1).
|
||||
Count(count).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("LineReverseSettingService GetPage error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取LineReverseSetting对象
|
||||
func (e *LineReverseSetting) Get(d *dto.LineReverseSettingGetReq, p *actions.DataPermission, model *models.LineReverseSetting) error {
|
||||
var data models.LineReverseSetting
|
||||
|
||||
err := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).
|
||||
First(model, d.GetId()).Error
|
||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = errors.New("查看对象不存在或无权查看")
|
||||
e.Log.Errorf("Service GetLineReverseSetting error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
e.Log.Errorf("db error:%s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert 创建LineReverseSetting对象
|
||||
func (e *LineReverseSetting) Insert(c *dto.LineReverseSettingInsertReq) error {
|
||||
var err error
|
||||
var data models.LineReverseSetting
|
||||
c.Generate(&data)
|
||||
err = e.Orm.Create(&data).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("LineReverseSettingService Insert error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = e.SaveCache(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 缓存数据
|
||||
func (e *LineReverseSetting) SaveCache(data models.LineReverseSetting) error {
|
||||
if data.Id == 0 {
|
||||
if err := e.Orm.First(&data).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := helper.DefaultRedis.SetHashWithTags(rediskey.ReverseSetting, &data); err != nil {
|
||||
e.Log.Errorf("redis error:%s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update 修改LineReverseSetting对象
|
||||
func (e *LineReverseSetting) Update(c *dto.LineReverseSettingUpdateReq, p *actions.DataPermission) error {
|
||||
var err error
|
||||
var data = models.LineReverseSetting{}
|
||||
e.Orm.Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).First(&data, c.GetId())
|
||||
c.Generate(&data)
|
||||
|
||||
db := e.Orm.Save(&data)
|
||||
if err = db.Error; err != nil {
|
||||
e.Log.Errorf("LineReverseSettingService Save error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权更新该数据")
|
||||
}
|
||||
if err = helper.DefaultRedis.SetHashWithTags(rediskey.ReverseSetting, data); err != nil {
|
||||
e.Log.Errorf("redis error:%s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove 删除LineReverseSetting
|
||||
func (e *LineReverseSetting) Remove(d *dto.LineReverseSettingDeleteReq, p *actions.DataPermission) error {
|
||||
var data models.LineReverseSetting
|
||||
|
||||
db := e.Orm.Model(&data).
|
||||
Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).Delete(&data, d.GetId())
|
||||
if err := db.Error; err != nil {
|
||||
e.Log.Errorf("Service RemoveLineReverseSetting error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权删除该数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -3,7 +3,7 @@ package service
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"go-admin/app/admin/models"
|
||||
@ -59,9 +59,9 @@ func (e *LineUserSetting) Get(d *dto.LineUserSettingGetReq, p *actions.DataPermi
|
||||
|
||||
// Insert 创建LineUserSetting对象
|
||||
func (e *LineUserSetting) Insert(c *dto.LineUserSettingInsertReq) error {
|
||||
var err error
|
||||
var data models.LineUserSetting
|
||||
c.Generate(&data)
|
||||
var err error
|
||||
var data models.LineUserSetting
|
||||
c.Generate(&data)
|
||||
err = e.Orm.Create(&data).Error
|
||||
if err != nil {
|
||||
e.Log.Errorf("LineUserSettingService Insert error:%s \r\n", err)
|
||||
@ -72,22 +72,22 @@ func (e *LineUserSetting) Insert(c *dto.LineUserSettingInsertReq) error {
|
||||
|
||||
// Update 修改LineUserSetting对象
|
||||
func (e *LineUserSetting) Update(c *dto.LineUserSettingUpdateReq, p *actions.DataPermission) error {
|
||||
var err error
|
||||
var data = models.LineUserSetting{}
|
||||
e.Orm.Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).First(&data, c.GetId())
|
||||
c.Generate(&data)
|
||||
var err error
|
||||
var data = models.LineUserSetting{}
|
||||
e.Orm.Scopes(
|
||||
actions.Permission(data.TableName(), p),
|
||||
).First(&data, c.GetId())
|
||||
c.Generate(&data)
|
||||
|
||||
db := e.Orm.Save(&data)
|
||||
if err = db.Error; err != nil {
|
||||
e.Log.Errorf("LineUserSettingService Save error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权更新该数据")
|
||||
}
|
||||
return nil
|
||||
db := e.Orm.Save(&data)
|
||||
if err = db.Error; err != nil {
|
||||
e.Log.Errorf("LineUserSettingService Save error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权更新该数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove 删除LineUserSetting
|
||||
@ -99,23 +99,11 @@ func (e *LineUserSetting) Remove(d *dto.LineUserSettingDeleteReq, p *actions.Dat
|
||||
actions.Permission(data.TableName(), p),
|
||||
).Delete(&data, d.GetId())
|
||||
if err := db.Error; err != nil {
|
||||
e.Log.Errorf("Service RemoveLineUserSetting error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权删除该数据")
|
||||
}
|
||||
e.Log.Errorf("Service RemoveLineUserSetting error:%s \r\n", err)
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
return errors.New("无权删除该数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefault 获取默认LineUserSetting对象
|
||||
func (e *LineUserSetting) GetDefault() (models.LineUserSetting, error) {
|
||||
var data models.LineUserSetting
|
||||
|
||||
if err := e.Orm.Model(&data).First(&data).Error; err != nil {
|
||||
e.Log.Errorf("GetDefault LineUserSetting error:%s \r\n", err)
|
||||
return data, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@ -86,11 +86,6 @@ func run() error {
|
||||
clearLogJob(db, ctx)
|
||||
})
|
||||
|
||||
//自动重启websocket
|
||||
utility.SafeGo(func() {
|
||||
reconnect(ctx)
|
||||
})
|
||||
|
||||
// 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, os.Interrupt)
|
||||
@ -138,33 +133,3 @@ func clearLogJob(db *gorm.DB, ctx context.Context) {
|
||||
fileservice.ClearLogs(db)
|
||||
}
|
||||
}
|
||||
|
||||
// 定时重连websocket
|
||||
func reconnect(ctx context.Context) error {
|
||||
ticker := time.NewTicker(time.Hour * 1)
|
||||
defer ticker.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
serverinit.RestartConnect()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartDeadCheck 假死检测
|
||||
func StartDeadCheck(ctx context.Context) {
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
serverinit.DeadCheck()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
package binancecode
|
||||
|
||||
// NEW
|
||||
// CANCELED 已撤
|
||||
// CALCULATED 订单 ADL 或爆仓
|
||||
// EXPIRED 订单失效
|
||||
// TRADE 交易
|
||||
// AMENDMENT 订单修改
|
||||
|
||||
const (
|
||||
//新开单
|
||||
EVENT_NEW = "NEW"
|
||||
//已撤单
|
||||
EVENT_CANCELED = "CANCELED"
|
||||
//订单 ADL 或爆仓
|
||||
EVENT_CALCULATED = "CALCULATED"
|
||||
//订单失效
|
||||
EVENT_EXPIRED = "EXPIRED"
|
||||
//交易中
|
||||
EVENT_TRADE = "TRADE"
|
||||
//订单修改
|
||||
EVENT_AMENDMENT = "AMENDMENT"
|
||||
)
|
||||
@ -1,20 +0,0 @@
|
||||
package binancecode
|
||||
|
||||
const (
|
||||
//限价单
|
||||
ORDER_TYPE_LIMIT = "LIMIT"
|
||||
//市价单
|
||||
ORDER_TYPE_MARKET = "MARKET"
|
||||
//止损限价单
|
||||
ORDER_TYPE_STOP = "STOP"
|
||||
//止损市价单
|
||||
ORDER_TYPE_STOP_MARKET = "STOP_MARKET"
|
||||
//止盈限价单
|
||||
ORDER_TYPE_TAKE_PROFIT = "TAKE_PROFIT"
|
||||
//止盈市价单
|
||||
ORDER_TYPE_TAKE_PROFIT_MARKET = "TAKE_PROFIT_MARKET"
|
||||
//跟踪止损单
|
||||
ORDER_TYPE_TRAILING_STOP_MARKET = "TRAILING_STOP_MARKET"
|
||||
//爆仓
|
||||
ORDER_TYPE_LIQUIDATION = "LIQUIDATION"
|
||||
)
|
||||
@ -1,6 +0,0 @@
|
||||
package binancecode
|
||||
|
||||
const (
|
||||
SideBuy = "BUY"
|
||||
SideSell = "SELL"
|
||||
)
|
||||
@ -1,6 +0,0 @@
|
||||
package rediskey
|
||||
|
||||
const (
|
||||
//主单api和反单关系 List {apiId}:{reverseApiId}
|
||||
ApiReverseRelation = "api_reverse_relation"
|
||||
)
|
||||
@ -1,11 +0,0 @@
|
||||
package rediskey
|
||||
|
||||
const (
|
||||
//反单仓位管理 {apiKey} hash key
|
||||
ReversePositionKey = "reverse_position:%s"
|
||||
)
|
||||
|
||||
const (
|
||||
//反单配置 hash {order_type} {take_profit_ratio} {stop_loss_ratio} {reverse_premium_ratio}
|
||||
ReverseSetting = "reverse_setting"
|
||||
)
|
||||
@ -12,7 +12,6 @@ import (
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// RedisHelper 结构体封装了 Redis 客户端及上下文
|
||||
@ -461,76 +460,44 @@ func (r *RedisHelper) SetNX(key string, value interface{}, expiration time.Durat
|
||||
return result == "OK", nil
|
||||
}
|
||||
|
||||
// SetHashWithTags 改进版:支持 struct 或 map 输入
|
||||
func (r *RedisHelper) SetHashWithTags(key string, obj interface{}) error {
|
||||
fields, err := getFieldsFromStruct(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.client.HSet(r.ctx, key, fields).Result()
|
||||
return err
|
||||
}
|
||||
|
||||
// getFieldsFromStruct 改进版:处理指针并进行类型检查,返回 error
|
||||
func getFieldsFromStruct(obj interface{}) (map[string]interface{}, error) {
|
||||
func getFieldsFromStruct(obj interface{}) map[string]interface{} {
|
||||
fields := make(map[string]interface{})
|
||||
val := reflect.ValueOf(obj)
|
||||
|
||||
// 如果 obj 是指针,则解引用
|
||||
if val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
// 确保我们正在处理的是一个结构体
|
||||
if val.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("期望一个结构体或结构体指针,但得到的是 %s", val.Kind())
|
||||
}
|
||||
|
||||
typ := val.Type() // 在解引用后获取类型
|
||||
typ := reflect.TypeOf(obj)
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
tag := field.Tag.Get("redis") // 获取 redis tag
|
||||
tag := field.Tag.Get("redis")
|
||||
if tag != "" {
|
||||
fieldVal := val.Field(i)
|
||||
// 检查字段是否可导出,不可导出的字段无法直接通过 Interface() 访问
|
||||
if !fieldVal.CanInterface() {
|
||||
continue // 跳过不可导出字段
|
||||
}
|
||||
|
||||
switch fieldVal.Kind() {
|
||||
case reflect.Slice, reflect.Map:
|
||||
if fieldVal.Kind() == reflect.Slice || fieldVal.Kind() == reflect.Map {
|
||||
// 处理切片或映射类型
|
||||
// 对于切片,使用索引作为字段名
|
||||
if fieldVal.Kind() == reflect.Slice {
|
||||
for j := 0; j < fieldVal.Len(); j++ {
|
||||
elem := fieldVal.Index(j).Interface()
|
||||
fields[fmt.Sprintf("%s_%d", tag, j)] = elem
|
||||
}
|
||||
} else if fieldVal.Kind() == reflect.Map {
|
||||
// 对于映射,使用键作为字段名
|
||||
for _, key := range fieldVal.MapKeys() {
|
||||
elem := fieldVal.MapIndex(key).Interface()
|
||||
fields[fmt.Sprintf("%s_%v", tag, key.Interface())] = elem
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
if fieldVal.Type() == reflect.TypeOf(decimal.Decimal{}) {
|
||||
if decVal, ok := fieldVal.Interface().(decimal.Decimal); ok {
|
||||
// 将 decimal.Decimal 直接转换为字符串来保留精度
|
||||
fields[tag] = decVal.String()
|
||||
} else {
|
||||
// 理论上不应该发生,但作为回退
|
||||
fields[tag] = fieldVal.Interface()
|
||||
}
|
||||
} else {
|
||||
fields[tag] = fieldVal.Interface()
|
||||
}
|
||||
default:
|
||||
} else {
|
||||
fields[tag] = fieldVal.Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields, nil
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func (r *RedisHelper) SetHashWithTags(key string, obj interface{}) error {
|
||||
fields := getFieldsFromStruct(obj)
|
||||
_, err := r.client.HSet(r.ctx, key, fields).Result()
|
||||
return err
|
||||
}
|
||||
|
||||
// HSetField 设置哈希中的一个字段
|
||||
@ -576,82 +543,6 @@ func (r *RedisHelper) HGetAllFields(key string) (map[string]string, error) {
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// HGetAsObject 获取哈希中所有字段的值并反序列化为对象
|
||||
func (r *RedisHelper) HGetAsObject(key string, out interface{}) error {
|
||||
data, err := r.HGetAllFields(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(out)
|
||||
if val.Kind() != reflect.Ptr || val.IsNil() {
|
||||
return fmt.Errorf("output must be a non-nil pointer to a struct")
|
||||
}
|
||||
val = val.Elem()
|
||||
typ := val.Type()
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
tag := field.Tag.Get("redis")
|
||||
if tag == "" {
|
||||
continue
|
||||
}
|
||||
strVal, ok := data[tag]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldVal := val.Field(i)
|
||||
if !fieldVal.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
switch fieldVal.Kind() {
|
||||
case reflect.String:
|
||||
fieldVal.SetString(strVal)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil {
|
||||
fieldVal.SetInt(intVal)
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if uintVal, err := strconv.ParseUint(strVal, 10, 64); err == nil {
|
||||
fieldVal.SetUint(uintVal)
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if floatVal, err := strconv.ParseFloat(strVal, 64); err == nil {
|
||||
fieldVal.SetFloat(floatVal)
|
||||
}
|
||||
case reflect.Bool:
|
||||
if boolVal, err := strconv.ParseBool(strVal); err == nil {
|
||||
fieldVal.SetBool(boolVal)
|
||||
}
|
||||
case reflect.Struct:
|
||||
// 针对 time.Time 特别处理
|
||||
if fieldVal.Type() == reflect.TypeOf(time.Time{}) {
|
||||
if t, err := time.Parse(time.RFC3339, strVal); err == nil {
|
||||
fieldVal.Set(reflect.ValueOf(t))
|
||||
}
|
||||
} else if fieldVal.Type() == reflect.TypeOf(decimal.Decimal{}) {
|
||||
if t, err := decimal.NewFromString(strVal); err == nil {
|
||||
fieldVal.Set(reflect.ValueOf(t))
|
||||
}
|
||||
} else {
|
||||
// 其他 struct 尝试用 json 反序列化
|
||||
ptr := reflect.New(fieldVal.Type()).Interface()
|
||||
if err := sonic.Unmarshal([]byte(strVal), ptr); err == nil {
|
||||
fieldVal.Set(reflect.ValueOf(ptr).Elem())
|
||||
}
|
||||
}
|
||||
case reflect.Slice, reflect.Map:
|
||||
ptr := reflect.New(fieldVal.Type()).Interface()
|
||||
if err := sonic.Unmarshal([]byte(strVal), ptr); err == nil {
|
||||
fieldVal.Set(reflect.ValueOf(ptr).Elem())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HDelField 删除哈希中的某个字段
|
||||
func (r *RedisHelper) HDelField(key, field string) error {
|
||||
_, err := r.client.HDel(r.ctx, key, field).Result()
|
||||
|
||||
@ -3,6 +3,7 @@ package helper
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go-admin/pkg/utility"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
@ -60,6 +61,8 @@ func InitLockRedisConn(host, password, dbIndex string) {
|
||||
log.Error("Failed to connect to Redis", zap.Error(err))
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("redis lock初始化完毕")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -24,27 +24,11 @@ import (
|
||||
|
||||
// 业务初始化
|
||||
func BusinessInit(db *gorm.DB) {
|
||||
|
||||
httputils.InitProxy(config.ExtConfig.ProxyUrl)
|
||||
|
||||
if err := loadApiUser(db); err != nil { //加载api用户
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
lineApiService := service.LineApiUser{}
|
||||
lineApiService.Orm = db
|
||||
lineApiService.Log = logger.NewHelper(sdk.Runtime.GetLogger()).WithFields(map[string]interface{}{})
|
||||
|
||||
if err := lineApiService.CacheRelation(); err != nil {
|
||||
lineApiService.Log.Errorf("初始化api关系失败 err:%v", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
reverseSettingService := service.LineReverseSetting{}
|
||||
reverseSettingService.Orm = db
|
||||
reverseSettingService.Log = lineApiService.Log
|
||||
|
||||
if err := reverseSettingService.SaveCache(models.LineReverseSetting{}); err != nil {
|
||||
reverseSettingService.Log.Errorf("初始化反单设置失败 err:%v", err)
|
||||
fmt.Printf("加载api用户失败 err:%v", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
|
||||
@ -84,44 +84,3 @@ func UserSubscribeInit(orm *gorm.DB, ctx context.Context) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 重启连接
|
||||
func RestartConnect() error {
|
||||
spotSockets := excservice.SpotSockets
|
||||
futuresSockets := excservice.FutureSockets
|
||||
timeOut := 22 * time.Hour
|
||||
|
||||
for _, item := range spotSockets {
|
||||
//超过22小时,重新连接
|
||||
if time.Since(item.ConnectTime) > timeOut {
|
||||
if err := item.ReplaceConnection(); err != nil {
|
||||
log.Errorf("现货重启连接失败 key:%s,error:%s", item.GetKey(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range futuresSockets {
|
||||
//超过22小时,重新连接
|
||||
if time.Since(item.ConnectTime) > timeOut {
|
||||
if err := item.ReplaceConnection(); err != nil {
|
||||
log.Errorf("合约重启连接失败 key:%s,error:%s", item.GetKey(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 假死重启
|
||||
func DeadCheck() {
|
||||
spotSockets := excservice.SpotSockets
|
||||
futuresSockets := excservice.FutureSockets
|
||||
|
||||
for _, item := range spotSockets {
|
||||
item.DeadCheck()
|
||||
}
|
||||
|
||||
for _, item := range futuresSockets {
|
||||
item.DeadCheck()
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,10 +37,6 @@ type TradeSet struct {
|
||||
E int64 `json:"-"` //推送时间
|
||||
}
|
||||
|
||||
func (e *TradeSet) GetSymbol() string {
|
||||
return e.Coin + e.Currency
|
||||
}
|
||||
|
||||
//CommissionType int `db:"commissiontype"` //手续费:1买,2卖,3双向
|
||||
//DepositNum float64 `db:"depositnum" json:"depositnum"` //保证金规模(手)
|
||||
//ForceRate float64 `db:"forcerate" json:"forcerate"` //维持保证金率1%
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
package maphelper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
func GetString(m map[string]interface{}, key string) (string, error) {
|
||||
val, ok := m[key]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("参数错误,缺少字段 %s", key)
|
||||
}
|
||||
strVal, ok := val.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("字段 %s 类型错误", key)
|
||||
}
|
||||
return strVal, nil
|
||||
}
|
||||
|
||||
func GetFloat64(m map[string]interface{}, key string) (float64, error) {
|
||||
val, ok := m[key]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("参数错误,缺少字段 %s", key)
|
||||
}
|
||||
f, ok := val.(float64)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("字段 %s 类型错误", key)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func GetBool(m map[string]interface{}, key string) bool {
|
||||
if val, ok := m[key].(bool); ok {
|
||||
return val
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetDecimal(m map[string]interface{}, key string) decimal.Decimal {
|
||||
if val, ok := m[key].(string); ok {
|
||||
d, _ := decimal.NewFromString(val)
|
||||
return d
|
||||
}
|
||||
return decimal.Zero
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
package retryhelper
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RetryOptions 定义重试的配置项
|
||||
type RetryOptions struct {
|
||||
MaxRetries int // 最大重试次数
|
||||
InitialInterval time.Duration // 初始重试间隔
|
||||
MaxInterval time.Duration // 最大重试间隔
|
||||
BackoffFactor float64 // 指数退避的增长因子
|
||||
RetryableErrFn func(error) bool // 用于判断是否为可重试错误的函数(可选)
|
||||
}
|
||||
|
||||
func DefaultRetryOptions() RetryOptions {
|
||||
return RetryOptions{
|
||||
MaxRetries: 3,
|
||||
InitialInterval: 150 * time.Millisecond,
|
||||
MaxInterval: 3 * time.Second,
|
||||
BackoffFactor: 2.0,
|
||||
}
|
||||
}
|
||||
|
||||
// Retry 重试通用函数(用于没有返回值的函数)
|
||||
func Retry(op func() error, opts RetryOptions) error {
|
||||
_, err := RetryWithResult(func() (struct{}, error) {
|
||||
return struct{}{}, op()
|
||||
}, opts)
|
||||
return err
|
||||
}
|
||||
|
||||
// RetryWithResult 重试通用函数(用于带返回值的函数)
|
||||
func RetryWithResult[T any](op func() (T, error), opts RetryOptions) (result T, err error) {
|
||||
interval := opts.InitialInterval
|
||||
for attempt := 0; attempt <= opts.MaxRetries; attempt++ {
|
||||
result, err = op()
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if opts.RetryableErrFn != nil && !opts.RetryableErrFn(err) {
|
||||
return result, err
|
||||
}
|
||||
|
||||
if attempt < opts.MaxRetries {
|
||||
time.Sleep(interval)
|
||||
interval = time.Duration(math.Min(float64(opts.MaxInterval), float64(interval)*opts.BackoffFactor))
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
@ -28,7 +28,3 @@ func init() {
|
||||
func GetOrderId() int64 {
|
||||
return snowNode.Generate().Int64()
|
||||
}
|
||||
|
||||
func GetOrderNo() string {
|
||||
return fmt.Sprintf("%d", GetOrderId())
|
||||
}
|
||||
|
||||
@ -467,21 +467,6 @@ func GetApiInfo(apiId int) (DbModels.LineApiUser, error) {
|
||||
return api, nil
|
||||
}
|
||||
|
||||
// GetReverseApiInfo 根据apiKey获取api用户信息
|
||||
func GetApiInfoByKey(apiKey string) DbModels.LineApiUser {
|
||||
result := DbModels.LineApiUser{}
|
||||
keys, _ := helper.DefaultRedis.GetAllKeysAndValues(fmt.Sprintf(rediskey.API_USER, "*"))
|
||||
|
||||
for _, key := range keys {
|
||||
if strings.Contains(key, apiKey) {
|
||||
sonic.UnmarshalString(key, &result)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
根据A账户获取B账号信息
|
||||
*/
|
||||
|
||||
@ -479,13 +479,3 @@ func GetOpenOrderSns(db *gorm.DB, mainIds []int) ([]string, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 回去反单默认配置
|
||||
func GetReverseSetting(db *gorm.DB) (DbModels.LineReverseSetting, error) {
|
||||
var setting DbModels.LineReverseSetting
|
||||
if err := db.Model(&DbModels.LineReverseSetting{}).First(&setting).Error; err != nil {
|
||||
return setting, err
|
||||
}
|
||||
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
@ -1,125 +0,0 @@
|
||||
package binanceservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
DbModels "go-admin/app/admin/models"
|
||||
"go-admin/pkg/retryhelper"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||
)
|
||||
|
||||
type FuturesResetV2 struct {
|
||||
service.Service
|
||||
}
|
||||
|
||||
// 带重试机制的合约下单
|
||||
func (e *FuturesResetV2) OrderPlaceLoop(apiUserInfo *DbModels.LineApiUser, params FutOrderPlace) error {
|
||||
opts := retryhelper.DefaultRetryOptions()
|
||||
opts.RetryableErrFn = func(err error) bool {
|
||||
if strings.Contains(err.Error(), "LOT_SIZE") {
|
||||
return false
|
||||
}
|
||||
//重试
|
||||
return true
|
||||
}
|
||||
|
||||
err := retryhelper.Retry(func() error {
|
||||
return e.OrderPlace(apiUserInfo, params)
|
||||
}, opts)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 合约下单
|
||||
func (e *FuturesResetV2) OrderPlace(apiUserInfo *DbModels.LineApiUser, params FutOrderPlace) error {
|
||||
if err := params.CheckParams(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
orderType := strings.ToUpper(params.OrderType)
|
||||
side := strings.ToUpper(params.Side)
|
||||
|
||||
paramsMaps := map[string]string{
|
||||
"symbol": params.Symbol,
|
||||
"side": side,
|
||||
"type": orderType,
|
||||
"newClientOrderId": params.NewClientOrderId,
|
||||
"positionSide": params.PositionSide,
|
||||
}
|
||||
|
||||
// 设置市价和限价等类型的参数
|
||||
switch orderType {
|
||||
case "LIMIT":
|
||||
paramsMaps["price"] = params.Price.String()
|
||||
paramsMaps["timeInForce"] = "GTC"
|
||||
case "TAKE_PROFIT_MARKET":
|
||||
paramsMaps["timeInForce"] = "GTC"
|
||||
paramsMaps["stopprice"] = params.Profit.String()
|
||||
paramsMaps["workingType"] = "MARK_PRICE"
|
||||
|
||||
if params.ClosePosition {
|
||||
paramsMaps["closePosition"] = "true"
|
||||
}
|
||||
case "TAKE_PROFIT":
|
||||
paramsMaps["price"] = params.Price.String()
|
||||
paramsMaps["stopprice"] = params.Profit.String()
|
||||
paramsMaps["timeInForce"] = "GTC"
|
||||
case "STOP_MARKET":
|
||||
paramsMaps["stopprice"] = params.StopPrice.String()
|
||||
paramsMaps["workingType"] = "MARK_PRICE"
|
||||
paramsMaps["timeInForce"] = "GTC"
|
||||
|
||||
if params.ClosePosition {
|
||||
paramsMaps["closePosition"] = "true"
|
||||
}
|
||||
case "STOP":
|
||||
paramsMaps["price"] = params.Price.String()
|
||||
paramsMaps["stopprice"] = params.StopPrice.String()
|
||||
paramsMaps["workingType"] = "MARK_PRICE"
|
||||
paramsMaps["timeInForce"] = "GTC"
|
||||
}
|
||||
|
||||
//不是平仓
|
||||
if !params.ClosePosition {
|
||||
paramsMaps["quantity"] = params.Quantity.String()
|
||||
}
|
||||
// 获取 API 信息和发送下单请求
|
||||
client := GetClient(apiUserInfo)
|
||||
_, statusCode, err := client.SendFuturesRequestAuth("/fapi/v1/order", "POST", paramsMaps)
|
||||
|
||||
if err != nil {
|
||||
return parseOrderError(err, paramsMaps, statusCode, apiUserInfo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 拆出的错误解析函数
|
||||
func parseOrderError(err error, paramsMaps map[string]string, statusCode int, apiUserInfo *DbModels.LineApiUser) error {
|
||||
var dataMap map[string]interface{}
|
||||
|
||||
if err2 := sonic.Unmarshal([]byte(err.Error()), &dataMap); err2 == nil {
|
||||
if code, ok := dataMap["code"]; ok {
|
||||
paramsVal, _ := sonic.MarshalString(¶msMaps)
|
||||
log.Error("下单失败 参数:", paramsVal)
|
||||
errContent := FutErrorMaps[code.(float64)]
|
||||
if errContent == "" {
|
||||
errContent, _ = dataMap["msg"].(string)
|
||||
}
|
||||
return fmt.Errorf("api_id:%d 交易对:%s 下单失败:%s", apiUserInfo.Id, paramsMaps["symbol"], errContent)
|
||||
}
|
||||
}
|
||||
|
||||
// 特殊处理保证金不足
|
||||
if strings.Contains(err.Error(), "Margin is insufficient.") {
|
||||
return fmt.Errorf("api_id:%d 交易对:%s 下单失败:%s", apiUserInfo.Id, paramsMaps["symbol"], FutErrorMaps[-2019])
|
||||
}
|
||||
|
||||
return fmt.Errorf("api_id:%d 交易对:%s statusCode:%v 下单失败:%s", apiUserInfo.Id, paramsMaps["symbol"], statusCode, err.Error())
|
||||
}
|
||||
@ -12,7 +12,6 @@ import (
|
||||
"go-admin/models/binancedto"
|
||||
"go-admin/models/futuresdto"
|
||||
"go-admin/pkg/httputils"
|
||||
"go-admin/pkg/retryhelper"
|
||||
"go-admin/pkg/utility"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -599,49 +598,6 @@ func (e FutRestApi) GetHoldeData(apiInfo *DbModels.LineApiUser, symbol, side str
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取合约 持仓价格、数量
|
||||
// symbol:交易对
|
||||
// positionSide:持仓方向
|
||||
// holdeData:持仓数据
|
||||
func (e FutRestApi) GetPositionData(apiInfo *DbModels.LineApiUser, symbol, positionSide string, holdeData *HoldeData) error {
|
||||
opts := retryhelper.DefaultRetryOptions()
|
||||
opts.RetryableErrFn = func(err error) bool {
|
||||
if strings.Contains(err.Error(), "LOT_SIZE") {
|
||||
return false
|
||||
}
|
||||
//重试
|
||||
return true
|
||||
}
|
||||
|
||||
holdes, err := retryhelper.RetryWithResult(func() ([]PositionRisk, error) {
|
||||
return e.GetPositionV3(apiInfo, symbol)
|
||||
}, opts)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, item := range holdes {
|
||||
positionAmount, _ := decimal.NewFromString(item.PositionAmt)
|
||||
if (positionSide == "LONG" && item.PositionSide == "BOTH" && positionAmount.Cmp(decimal.Zero) > 0) || item.PositionSide == positionSide { //多
|
||||
holdeData.AveragePrice, _ = decimal.NewFromString(item.EntryPrice)
|
||||
holdeData.TotalQuantity = positionAmount.Abs()
|
||||
continue
|
||||
} else if (positionSide == "SHORT" && item.PositionSide == "BOTH" && positionAmount.Cmp(decimal.Zero) < 0) || item.PositionSide == positionSide { //空
|
||||
holdeData.AveragePrice, _ = decimal.NewFromString(item.EntryPrice)
|
||||
holdeData.TotalQuantity = positionAmount.Abs()
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if holdeData.AveragePrice.Cmp(decimal.Zero) == 0 {
|
||||
holdesVal, _ := sonic.MarshalString(&holdes)
|
||||
log.Error("均价错误 symbol:", symbol, " 数据:", holdesVal)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取代币持仓信息
|
||||
func getSymbolHolde(e FutRestApi, apiInfo *DbModels.LineApiUser, symbol string, side string, holdeData *HoldeData) ([]PositionRisk, error) {
|
||||
holdes, err := e.GetPositionV3(apiInfo, symbol)
|
||||
@ -759,15 +715,6 @@ func (e FutRestApi) ClosePosition(symbol string, orderSn string, quantity decima
|
||||
return nil
|
||||
}
|
||||
|
||||
// 带重试机制的取消订单
|
||||
func (e FutRestApi) CancelFutOrderRetry(apiUserInfo DbModels.LineApiUser, symbol string, newClientOrderId string) error {
|
||||
opts := retryhelper.DefaultRetryOptions()
|
||||
|
||||
return retryhelper.Retry(func() error {
|
||||
return e.CancelFutOrder(apiUserInfo, symbol, newClientOrderId)
|
||||
}, opts)
|
||||
}
|
||||
|
||||
// CancelFutOrder 通过单个订单号取消合约委托
|
||||
// symbol 交易对
|
||||
// newClientOrderId 系统自定义订单号
|
||||
@ -827,27 +774,6 @@ func (e FutRestApi) CancelAllFutOrder(apiUserInfo DbModels.LineApiUser, symbol s
|
||||
return nil
|
||||
}
|
||||
|
||||
// 带重试的批量撤销订单
|
||||
func (e FutRestApi) CancelBatchFutOrderLoop(apiUserInfo DbModels.LineApiUser, symbol string, newClientOrderIdList []string) error {
|
||||
opts := retryhelper.DefaultRetryOptions()
|
||||
opts.RetryableErrFn = func(err error) bool {
|
||||
if strings.Contains(err.Error(), "LOT_SIZE") {
|
||||
return false
|
||||
}
|
||||
//重试
|
||||
return true
|
||||
}
|
||||
|
||||
err := retryhelper.Retry(func() error {
|
||||
return e.CancelBatchFutOrder(apiUserInfo, symbol, newClientOrderIdList)
|
||||
}, opts)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CancelBatchFutOrder 批量撤销订单
|
||||
// symbol 交易对
|
||||
// newClientOrderIdList 系统自定义的订单号, 最多支持10个订单
|
||||
|
||||
@ -27,7 +27,7 @@ import (
|
||||
/*
|
||||
修改订单信息
|
||||
*/
|
||||
func ChangeFutureOrder(mapData map[string]interface{}, apiKey string) {
|
||||
func ChangeFutureOrder(mapData map[string]interface{}) {
|
||||
// 检查订单号是否存在
|
||||
orderSn, ok := mapData["c"]
|
||||
originOrderSn := mapData["C"] //取消操作 代表原始订单号
|
||||
@ -61,15 +61,34 @@ func ChangeFutureOrder(mapData map[string]interface{}, apiKey string) {
|
||||
}
|
||||
defer lock.Release()
|
||||
|
||||
//反单逻辑
|
||||
reverseService := ReverseService{}
|
||||
reverseService.Orm = db
|
||||
reverseService.Log = logger.NewHelper(logger.DefaultLogger)
|
||||
_, err = reverseService.ReverseOrder(apiKey, mapData)
|
||||
|
||||
// 查询订单
|
||||
preOrder, err := getPreOrder(db, orderSn)
|
||||
if err != nil {
|
||||
logger.Errorf("合约订单回调失败,反单失败:%v", err)
|
||||
logger.Error("合约订单回调失败,查询订单失败:", orderSn, " err:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 解析订单状态
|
||||
status, ok := mapData["X"].(string)
|
||||
if !ok {
|
||||
mapStr, _ := sonic.Marshal(&mapData)
|
||||
logger.Error("订单回调失败,没有状态:", string(mapStr))
|
||||
return
|
||||
}
|
||||
// 更新订单状态
|
||||
orderStatus, reason := parseOrderStatus(status, mapData)
|
||||
|
||||
if orderStatus == 0 {
|
||||
logger.Error("订单回调失败,状态错误:", orderSn, " status:", status, " reason:", reason)
|
||||
return
|
||||
}
|
||||
|
||||
if err := updateOrderStatus(db, preOrder, orderStatus, reason, true, mapData); err != nil {
|
||||
logger.Error("修改订单状态失败:", orderSn, " err:", err)
|
||||
return
|
||||
}
|
||||
|
||||
handleFutOrderByType(db, preOrder, orderStatus)
|
||||
}
|
||||
|
||||
// 合约回调
|
||||
|
||||
@ -61,19 +61,17 @@ type EntryPriceResult struct {
|
||||
}
|
||||
|
||||
type FutOrderPlace struct {
|
||||
ApiId int `json:"api_id"` //api用户id
|
||||
Symbol string `json:"symbol"` //合约交易对
|
||||
Side string `json:"side"` //购买方向
|
||||
PositionSide string `json:"position_side"` //持仓方向
|
||||
Quantity decimal.Decimal `json:"quantity"` //数量
|
||||
Price decimal.Decimal `json:"price"` //限价单价
|
||||
SideType string `json:"side_type"` //现价或者市价
|
||||
OpenOrder int `json:"open_order"` //是否开启限价单止盈止损
|
||||
Profit decimal.Decimal `json:"profit"` //止盈价格
|
||||
StopPrice decimal.Decimal `json:"stopprice"` //止损价格
|
||||
OrderType string `json:"order_type"` //订单类型,市价或限价MARKET(市价单) TAKE_PROFIT_MARKET(市价止盈) TAKE_PROFIT(限价止盈) STOP (限价止损) STOP_MARKET(市价止损)
|
||||
ApiId int `json:"api_id"` //api用户id
|
||||
Symbol string `json:"symbol"` //合约交易对
|
||||
Side string `json:"side"` //购买方向
|
||||
Quantity decimal.Decimal `json:"quantity"` //数量
|
||||
Price decimal.Decimal `json:"price"` //限价单价
|
||||
SideType string `json:"side_type"` //现价或者市价
|
||||
OpenOrder int `json:"open_order"` //是否开启限价单止盈止损
|
||||
Profit decimal.Decimal `json:"profit"` //止盈价格
|
||||
StopPrice decimal.Decimal `json:"stopprice"` //止损价格
|
||||
OrderType string `json:"order_type"` //订单类型,市价或限价MARKET(市价单) TAKE_PROFIT_MARKET(市价止盈) TAKE_PROFIT(限价止盈) STOP (限价止损) STOP_MARKET(市价止损)
|
||||
NewClientOrderId string `json:"newClientOrderId"`
|
||||
ClosePosition bool `json:"closePosition"` //是否平仓
|
||||
}
|
||||
|
||||
func (s FutOrderPlace) CheckParams() error {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,34 +0,0 @@
|
||||
package binanceservice
|
||||
|
||||
import (
|
||||
"go-admin/common/helper"
|
||||
"testing"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func initConfig() *gorm.DB {
|
||||
dsn := "root:123456@tcp(127.0.0.1:3306)/go_exchange_single?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
||||
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
sdk.Runtime.SetDb("default", db)
|
||||
helper.InitDefaultRedis("127.0.0.1:6379", "", 2)
|
||||
helper.InitLockRedisConn("127.0.0.1:6379", "", "2")
|
||||
|
||||
return db
|
||||
}
|
||||
func TestReverseOrder(t *testing.T) {
|
||||
db := initConfig()
|
||||
mapData := make(map[string]interface{})
|
||||
content := `{"s":"ADAUSDT","c":"439301585084878848","S":"SELL","o":"LIMIT","f":"GTC","q":"8","p":"0.7781","ap":"0.7843","sp":"0","x":"TRADE","X":"FILLED","i":56468022240,"l":"8","z":"8","L":"0.7843","n":"0.0031372","N":"USDT","T":1753950025820,"t":1642816051,"b":"0","a":"0","m":false,"R":false,"wt":"CONTRACT_PRICE","ot":"LIMIT","ps":"SHORT","cp":false,"rp":"0","pP":false,"si":0,"ss":0,"V":"EXPIRE_MAKER","pm":"NONE","gtd":0}`
|
||||
sonic.Unmarshal([]byte(content), &mapData)
|
||||
|
||||
service := ReverseService{}
|
||||
service.Orm = db
|
||||
service.Log = logger.NewHelper(logger.DefaultLogger)
|
||||
|
||||
service.ReverseOrder("c8ej2vxXzNUIlCjQCmU1iavK8LG78uZaXY3CIT4kz0PuhnXycg44HsVAbsqupHTw", mapData)
|
||||
}
|
||||
@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@ -28,7 +29,7 @@ import (
|
||||
/*
|
||||
订单回调
|
||||
*/
|
||||
func ChangeSpotOrder(mapData map[string]interface{}, apiKey string) {
|
||||
func ChangeSpotOrder(mapData map[string]interface{}) {
|
||||
// 检查订单号是否存在
|
||||
orderSn, ok := mapData["c"]
|
||||
originOrderSn := mapData["C"] //取消操作 代表原始订单号
|
||||
@ -192,7 +193,7 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
|
||||
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotReduceCallback, preOrder.ApiId, preOrder.Symbol), 120, 20, 100*time.Millisecond)
|
||||
|
||||
if ok, err := lock.AcquireWait(context.Background()); err != nil {
|
||||
logger.Error("获取锁失败", err)
|
||||
log.Error("获取锁失败", err)
|
||||
return
|
||||
} else if ok {
|
||||
defer lock.Release()
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
package cacheservice
|
||||
|
||||
import (
|
||||
"go-admin/common/helper"
|
||||
"testing"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func TestGetReverseSetting(t *testing.T) {
|
||||
dsn := "root:123456@tcp(127.0.0.1:3306)/go_exchange_single?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
|
||||
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
helper.InitDefaultRedis("127.0.0.1:6379", "", 2)
|
||||
|
||||
setting, err := GetReverseSetting(db)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
t.Log(setting)
|
||||
}
|
||||
@ -130,22 +130,3 @@ func ResetSystemSetting(db *gorm.DB) (models.LineSystemSetting, error) {
|
||||
}
|
||||
return models.LineSystemSetting{}, nil
|
||||
}
|
||||
|
||||
// 获取反单配置
|
||||
func GetReverseSetting(db *gorm.DB) (models.LineReverseSetting, error) {
|
||||
setting := models.LineReverseSetting{}
|
||||
|
||||
helper.DefaultRedis.HGetAsObject(rediskey.ReverseSetting, &setting)
|
||||
|
||||
if setting.ReverseOrderType == "" {
|
||||
if err := db.Model(&setting).First(&setting).Error; err != nil {
|
||||
logger.Error("获取反单配置失败", err)
|
||||
}
|
||||
|
||||
if err := helper.DefaultRedis.SetHashWithTags(rediskey.ReverseSetting, &setting); err != nil {
|
||||
logger.Error("redis添加反单配置失败", err)
|
||||
}
|
||||
}
|
||||
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ import (
|
||||
- @msg 消息内容
|
||||
- @listenType 订阅类型 0-现货 1-合约
|
||||
*/
|
||||
func ReceiveListen(msg []byte, listenType int, apiKey string) (reconnect bool, err error) {
|
||||
func ReceiveListen(msg []byte, listenType int) (reconnect bool, err error) {
|
||||
var dataMap map[string]interface{}
|
||||
err = sonic.Unmarshal(msg, &dataMap)
|
||||
|
||||
@ -52,7 +52,7 @@ func ReceiveListen(msg []byte, listenType int, apiKey string) (reconnect bool, e
|
||||
}
|
||||
|
||||
utility.SafeGo(func() {
|
||||
binanceservice.ChangeSpotOrder(mapData, apiKey)
|
||||
binanceservice.ChangeSpotOrder(mapData)
|
||||
})
|
||||
} else {
|
||||
var data futuresdto.OrderTradeUpdate
|
||||
@ -64,7 +64,7 @@ func ReceiveListen(msg []byte, listenType int, apiKey string) (reconnect bool, e
|
||||
}
|
||||
|
||||
utility.SafeGo(func() {
|
||||
binanceservice.ChangeFutureOrder(data.OrderDetails, apiKey)
|
||||
binanceservice.ChangeFutureOrder(data.OrderDetails)
|
||||
})
|
||||
}
|
||||
//订单更新
|
||||
@ -72,9 +72,9 @@ func ReceiveListen(msg []byte, listenType int, apiKey string) (reconnect bool, e
|
||||
log.Info("executionReport 推送:", string(msg))
|
||||
|
||||
if listenType == 0 { //现货
|
||||
binanceservice.ChangeSpotOrder(dataMap, apiKey)
|
||||
binanceservice.ChangeSpotOrder(dataMap)
|
||||
} else if listenType == 1 { //合约
|
||||
binanceservice.ChangeFutureOrder(dataMap, apiKey)
|
||||
binanceservice.ChangeFutureOrder(dataMap)
|
||||
} else {
|
||||
log.Error("executionReport 不支持的订阅类型", strconv.Itoa(listenType))
|
||||
}
|
||||
@ -101,7 +101,6 @@ func ReceiveListen(msg []byte, listenType int, apiKey string) (reconnect bool, e
|
||||
|
||||
case "eventStreamTerminated":
|
||||
log.Info("账户数据流被终止 type:", getWsTypeName(listenType))
|
||||
return true, nil
|
||||
default:
|
||||
log.Info("未知事件 内容:", string(msg))
|
||||
log.Info("未知事件", event)
|
||||
|
||||
@ -20,7 +20,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
@ -43,9 +42,7 @@ type BinanceWebSocketManager struct {
|
||||
isStopped bool // 标记 WebSocket 是否已主动停止
|
||||
mu sync.Mutex // 用于控制并发访问 isStopped
|
||||
cancelFunc context.CancelFunc
|
||||
listenKey string // 新增字段
|
||||
reconnecting atomic.Bool // 防止重复重连
|
||||
ConnectTime time.Time // 当前连接建立时间
|
||||
listenKey string // 新增字段
|
||||
}
|
||||
|
||||
// 已有连接
|
||||
@ -75,10 +72,6 @@ func NewBinanceWebSocketManager(wsType int, apiKey, apiSecret, proxyType, proxyA
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *BinanceWebSocketManager) GetKey() string {
|
||||
return wm.apiKey
|
||||
}
|
||||
|
||||
func (wm *BinanceWebSocketManager) Start() {
|
||||
utility.SafeGo(wm.run)
|
||||
// wm.run()
|
||||
@ -98,33 +91,14 @@ func (wm *BinanceWebSocketManager) Restart(apiKey, apiSecret, proxyType, proxyAd
|
||||
wm.isStopped = false
|
||||
utility.SafeGo(wm.run)
|
||||
} else {
|
||||
log.Warnf("调用restart")
|
||||
wm.triggerReconnect(true)
|
||||
wm.reconnect <- struct{}{}
|
||||
}
|
||||
|
||||
return wm
|
||||
}
|
||||
|
||||
// 触发重连
|
||||
func (wm *BinanceWebSocketManager) triggerReconnect(force bool) {
|
||||
if force {
|
||||
wm.reconnecting.Store(false) // 强制重置标志位
|
||||
}
|
||||
|
||||
if wm.reconnecting.CompareAndSwap(false, true) {
|
||||
log.Warnf("准备重连 key: %s wsType: %v", wm.apiKey, wm.wsType)
|
||||
// 发送信号触发重连协程
|
||||
select {
|
||||
case wm.reconnect <- struct{}{}:
|
||||
default:
|
||||
// 防止阻塞,如果通道满了就跳过
|
||||
log.Debugf("reconnect 信号已存在,跳过 key:%s", wm.apiKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
func Restart(wm *BinanceWebSocketManager) {
|
||||
log.Warnf("调用restart")
|
||||
wm.triggerReconnect(true)
|
||||
wm.reconnect <- struct{}{}
|
||||
}
|
||||
func (wm *BinanceWebSocketManager) run() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@ -227,8 +201,6 @@ func (wm *BinanceWebSocketManager) connect(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 连接成功,更新连接时间
|
||||
wm.ConnectTime = time.Now()
|
||||
log.Info(fmt.Sprintf("已连接到 Binance %s WebSocket【%s】 key:%s", getWsTypeName(wm.wsType), wm.apiKey, listenKey))
|
||||
|
||||
// Ping处理
|
||||
@ -250,88 +222,10 @@ func (wm *BinanceWebSocketManager) connect(ctx context.Context) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
// utility.SafeGoParam(wm.restartConnect, ctx)
|
||||
utility.SafeGo(func() { wm.startListenKeyRenewal2(ctx) })
|
||||
utility.SafeGo(func() { wm.readMessages(ctx) })
|
||||
utility.SafeGo(func() { wm.handleReconnect(ctx) })
|
||||
utility.SafeGo(func() { wm.startPingLoop(ctx) })
|
||||
// utility.SafeGo(func() { wm.startDeadCheck(ctx) })
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReplaceConnection 创建新连接并关闭旧连接,实现无缝连接替换
|
||||
func (wm *BinanceWebSocketManager) ReplaceConnection() error {
|
||||
wm.mu.Lock()
|
||||
if wm.isStopped {
|
||||
wm.mu.Unlock()
|
||||
return errors.New("WebSocket 已停止")
|
||||
}
|
||||
oldCtxCancel := wm.cancelFunc
|
||||
oldConn := wm.ws
|
||||
wm.mu.Unlock()
|
||||
|
||||
log.Infof("🔄 正在替换连接: %s", wm.apiKey)
|
||||
|
||||
// 步骤 1:先获取新的 listenKey 和连接
|
||||
newListenKey, err := wm.getListenKey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取新 listenKey 失败: %w", err)
|
||||
}
|
||||
|
||||
dialer, err := wm.getDialer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newURL := fmt.Sprintf("%s/%s", wm.url, newListenKey)
|
||||
newConn, _, err := dialer.Dial(newURL, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("新连接 Dial 失败: %w", err)
|
||||
}
|
||||
|
||||
// 步骤 2:创建新的上下文并启动协程
|
||||
newCtx, newCancel := context.WithCancel(context.Background())
|
||||
|
||||
// 设置 ping handler
|
||||
newConn.SetPingHandler(func(appData string) error {
|
||||
log.Infof("收到 Ping(新连接) key:%s msg:%s", wm.apiKey, appData)
|
||||
for x := 0; x < 5; x++ {
|
||||
if err := newConn.WriteControl(websocket.PongMessage, []byte(appData), time.Now().Add(10*time.Second)); err != nil {
|
||||
log.Errorf("Pong 失败 %d 次 err:%v", x, err)
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
setLastTime(wm)
|
||||
return nil
|
||||
})
|
||||
|
||||
// 步骤 3:安全切换连接
|
||||
wm.mu.Lock()
|
||||
wm.ws = newConn
|
||||
wm.listenKey = newListenKey
|
||||
wm.ConnectTime = time.Now()
|
||||
wm.cancelFunc = newCancel
|
||||
wm.mu.Unlock()
|
||||
|
||||
log.Infof("✅ 替换连接成功: %s listenKey: %s", wm.apiKey, newListenKey)
|
||||
|
||||
// 步骤 4:启动新连接协程
|
||||
go wm.startListenKeyRenewal2(newCtx)
|
||||
go wm.readMessages(newCtx)
|
||||
go wm.handleReconnect(newCtx)
|
||||
go wm.startPingLoop(newCtx)
|
||||
// go wm.startDeadCheck(newCtx)
|
||||
|
||||
// 步骤 5:关闭旧连接、取消旧协程
|
||||
if oldCtxCancel != nil {
|
||||
oldCtxCancel()
|
||||
}
|
||||
if oldConn != nil {
|
||||
_ = oldConn.Close()
|
||||
log.Infof("🔒 旧连接已关闭: %s", wm.apiKey)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -357,7 +251,6 @@ func setLastTime(wm *BinanceWebSocketManager) {
|
||||
if val != "" {
|
||||
helper.DefaultRedis.SetString(subKey, val)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (wm *BinanceWebSocketManager) getDialer() (*websocket.Dialer, error) {
|
||||
@ -464,8 +357,7 @@ func (wm *BinanceWebSocketManager) readMessages(ctx context.Context) {
|
||||
_, msg, err := wm.ws.ReadMessage()
|
||||
if err != nil && strings.Contains(err.Error(), "websocket: close") {
|
||||
if !wm.isStopped {
|
||||
log.Error("收到关闭消息", err.Error())
|
||||
wm.triggerReconnect(false)
|
||||
wm.reconnect <- struct{}{}
|
||||
}
|
||||
|
||||
log.Error("websocket 关闭")
|
||||
@ -483,13 +375,11 @@ func (wm *BinanceWebSocketManager) readMessages(ctx context.Context) {
|
||||
func (wm *BinanceWebSocketManager) handleOrderUpdate(msg []byte) {
|
||||
setLastTime(wm)
|
||||
|
||||
if reconnect, _ := ReceiveListen(msg, wm.wsType, wm.apiKey); reconnect {
|
||||
log.Errorf("收到重连请求")
|
||||
wm.triggerReconnect(false)
|
||||
if reconnect, _ := ReceiveListen(msg, wm.wsType); reconnect {
|
||||
wm.reconnect <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop 安全停止 WebSocket
|
||||
func (wm *BinanceWebSocketManager) Stop() {
|
||||
wm.mu.Lock()
|
||||
defer wm.mu.Unlock()
|
||||
@ -497,8 +387,9 @@ func (wm *BinanceWebSocketManager) Stop() {
|
||||
if wm.isStopped {
|
||||
return
|
||||
}
|
||||
wm.isStopped = true
|
||||
|
||||
wm.isStopped = true
|
||||
// 关闭 stopChannel(确保已经关闭,避免 panic)
|
||||
select {
|
||||
case <-wm.stopChannel:
|
||||
default:
|
||||
@ -507,182 +398,107 @@ func (wm *BinanceWebSocketManager) Stop() {
|
||||
|
||||
if wm.cancelFunc != nil {
|
||||
wm.cancelFunc()
|
||||
wm.cancelFunc = nil
|
||||
}
|
||||
|
||||
if wm.ws != nil {
|
||||
if err := wm.ws.Close(); err != nil {
|
||||
log.Errorf("WebSocket Close 错误 key:%s err:%v", wm.apiKey, err)
|
||||
log.Error(fmt.Sprintf("key【%s】close失败", wm.apiKey), err)
|
||||
} else {
|
||||
log.Info(fmt.Sprintf("key【%s】close", wm.apiKey))
|
||||
}
|
||||
wm.ws = nil
|
||||
}
|
||||
|
||||
log.Infof("WebSocket 已完全停止 key:%s", wm.apiKey)
|
||||
wm.stopChannel = make(chan struct{}, 10)
|
||||
// **重新创建 stopChannel,避免 Restart() 时无效**
|
||||
wm.stopChannel = make(chan struct{})
|
||||
}
|
||||
|
||||
// handleReconnect 使用指数退避并保持永不退出
|
||||
// 重连机制
|
||||
func (wm *BinanceWebSocketManager) handleReconnect(ctx context.Context) {
|
||||
const maxRetries = 100
|
||||
baseDelay := time.Second * 2
|
||||
maxRetries := 5 // 最大重试次数
|
||||
retryCount := 0
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Infof("handleReconnect context done: %s", wm.apiKey)
|
||||
return
|
||||
|
||||
case <-wm.reconnect:
|
||||
wm.mu.Lock()
|
||||
if wm.isStopped {
|
||||
wm.mu.Unlock()
|
||||
return
|
||||
}
|
||||
wm.mu.Unlock()
|
||||
|
||||
log.Warnf("WebSocket 连接断开,准备重连 key:%s", wm.apiKey)
|
||||
log.Warn("WebSocket 连接断开,尝试重连...")
|
||||
|
||||
if wm.ws != nil {
|
||||
wm.ws.Close()
|
||||
}
|
||||
|
||||
// 取消旧的上下文
|
||||
if wm.cancelFunc != nil {
|
||||
wm.cancelFunc()
|
||||
}
|
||||
|
||||
for {
|
||||
wm.mu.Lock()
|
||||
if wm.ws != nil {
|
||||
_ = wm.ws.Close()
|
||||
wm.ws = nil
|
||||
}
|
||||
if wm.cancelFunc != nil {
|
||||
wm.cancelFunc()
|
||||
wm.cancelFunc = nil
|
||||
}
|
||||
wm.mu.Unlock()
|
||||
|
||||
newCtx, cancel := context.WithCancel(context.Background())
|
||||
wm.mu.Lock()
|
||||
wm.cancelFunc = cancel
|
||||
wm.mu.Unlock()
|
||||
wm.cancelFunc = cancel // 更新 cancelFunc
|
||||
|
||||
if err := wm.connect(newCtx); err != nil {
|
||||
log.Errorf("🔌 重连失败(%d/%d)key:%s,err: %v", retryCount+1, maxRetries, wm.apiKey, err)
|
||||
log.Errorf("重连失败: %v", err)
|
||||
cancel()
|
||||
retryCount++
|
||||
|
||||
if retryCount >= maxRetries {
|
||||
log.Errorf("❌ 重连失败次数过多,停止重连逻辑 key:%s", wm.apiKey)
|
||||
wm.reconnecting.Store(false)
|
||||
log.Error("重连失败次数过多,退出重连逻辑")
|
||||
return
|
||||
}
|
||||
|
||||
delay := baseDelay * time.Duration(1<<retryCount)
|
||||
if delay > time.Minute*5 {
|
||||
delay = time.Minute * 5
|
||||
}
|
||||
log.Warnf("等待 %v 后重试...", delay)
|
||||
time.Sleep(delay)
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Infof("✅ 重连成功 key:%s", wm.apiKey)
|
||||
retryCount = 0
|
||||
wm.reconnecting.Store(false)
|
||||
|
||||
// ✅ 重连成功后开启假死检测
|
||||
utility.SafeGo(func() { wm.startDeadCheck(newCtx) })
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// startDeadCheck 替代 Start 中的定时器,绑定连接生命周期
|
||||
func (wm *BinanceWebSocketManager) startDeadCheck(ctx context.Context) {
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
if wm.isStopped {
|
||||
return
|
||||
}
|
||||
wm.DeadCheck()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 假死检测
|
||||
func (wm *BinanceWebSocketManager) DeadCheck() {
|
||||
subKey := fmt.Sprintf(global.USER_SUBSCRIBE, wm.apiKey)
|
||||
val, _ := helper.DefaultRedis.GetString(subKey)
|
||||
if val == "" {
|
||||
log.Warnf("没有订阅信息,无法进行假死检测")
|
||||
return
|
||||
}
|
||||
|
||||
var data binancedto.UserSubscribeState
|
||||
_ = sonic.Unmarshal([]byte(val), &data)
|
||||
|
||||
var lastTime *time.Time
|
||||
if wm.wsType == 0 {
|
||||
lastTime = data.SpotLastTime
|
||||
} else {
|
||||
lastTime = data.FuturesLastTime
|
||||
}
|
||||
|
||||
// 定义最大静默时间(超出视为假死)
|
||||
var timeout time.Duration
|
||||
if wm.wsType == 0 {
|
||||
timeout = 40 * time.Second // Spot 每 20s ping,40s 足够
|
||||
} else {
|
||||
timeout = 6 * time.Minute // Futures 每 3 分钟 ping
|
||||
}
|
||||
|
||||
if lastTime != nil && time.Since(*lastTime) > timeout {
|
||||
log.Warnf("检测到假死连接 key:%s type:%v, 距离上次通信: %v, 触发重连", wm.apiKey, wm.wsType, time.Since(*lastTime))
|
||||
wm.triggerReconnect(true)
|
||||
}
|
||||
}
|
||||
|
||||
// 主动心跳发送机制
|
||||
func (wm *BinanceWebSocketManager) startPingLoop(ctx context.Context) {
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
if wm.isStopped {
|
||||
return
|
||||
}
|
||||
err := wm.ws.WriteMessage(websocket.PingMessage, []byte("ping"))
|
||||
if err != nil {
|
||||
log.Error("主动 Ping Binance 失败:", err)
|
||||
wm.triggerReconnect(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 定期删除listenkey 并重启ws
|
||||
// func (wm *BinanceWebSocketManager) startListenKeyRenewal(ctx context.Context, listenKey string) {
|
||||
// time.Sleep(30 * time.Minute)
|
||||
func (wm *BinanceWebSocketManager) startListenKeyRenewal(ctx context.Context, listenKey string) {
|
||||
time.Sleep(30 * time.Minute)
|
||||
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// return
|
||||
// default:
|
||||
// if err := wm.deleteListenKey(listenKey); err != nil {
|
||||
// log.Error("Failed to renew listenKey: ,type:%v key: %s", wm.wsType, wm.apiKey, err)
|
||||
// } else {
|
||||
// log.Debug("Successfully delete listenKey")
|
||||
// wm.triggerReconnect()
|
||||
// }
|
||||
// }
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
if err := wm.deleteListenKey(listenKey); err != nil {
|
||||
log.Error("Failed to renew listenKey: ,type:%v key: %s", wm.wsType, wm.apiKey, err)
|
||||
} else {
|
||||
log.Debug("Successfully delete listenKey")
|
||||
wm.reconnect <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// }
|
||||
// ticker := time.NewTicker(5 * time.Minute)
|
||||
// defer ticker.Stop()
|
||||
|
||||
// for {
|
||||
// select {
|
||||
// case <-ticker.C:
|
||||
// if wm.isStopped {
|
||||
// return
|
||||
// }
|
||||
|
||||
// if err := wm.deleteListenKey(listenKey); err != nil {
|
||||
// log.Error("Failed to renew listenKey: ,type:%v key: %s", wm.wsType, wm.apiKey, err)
|
||||
// } else {
|
||||
// log.Debug("Successfully delete listenKey")
|
||||
// wm.reconnect <- struct{}{}
|
||||
// return
|
||||
// }
|
||||
// case <-ctx.Done():
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// 定时续期
|
||||
func (wm *BinanceWebSocketManager) startListenKeyRenewal2(ctx context.Context) {
|
||||
@ -711,34 +527,49 @@ func (wm *BinanceWebSocketManager) startListenKeyRenewal2(ctx context.Context) {
|
||||
/*
|
||||
删除listenkey
|
||||
*/
|
||||
// func (wm *BinanceWebSocketManager) deleteListenKey(listenKey string) error {
|
||||
// client, err := wm.createBinanceClient()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
func (wm *BinanceWebSocketManager) deleteListenKey(listenKey string) error {
|
||||
client, err := wm.createBinanceClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// var resp []byte
|
||||
var resp []byte
|
||||
|
||||
// switch wm.wsType {
|
||||
// case 0:
|
||||
// path := fmt.Sprintf("/api/v3/userDataStream")
|
||||
// params := map[string]interface{}{
|
||||
// "listenKey": listenKey,
|
||||
// }
|
||||
// resp, _, err = client.SendSpotRequestByKey(path, "DELETE", params)
|
||||
switch wm.wsType {
|
||||
case 0:
|
||||
path := fmt.Sprintf("/api/v3/userDataStream")
|
||||
params := map[string]interface{}{
|
||||
"listenKey": listenKey,
|
||||
}
|
||||
resp, _, err = client.SendSpotRequestByKey(path, "DELETE", params)
|
||||
|
||||
// log.Debug(fmt.Sprintf("deleteListenKey resp: %s", string(resp)))
|
||||
// case 1:
|
||||
// resp, _, err = client.SendFuturesRequestByKey("/fapi/v1/listenKey", "DELETE", nil)
|
||||
// log.Debug(fmt.Sprintf("deleteListenKey resp: %s", string(resp)))
|
||||
// default:
|
||||
// return errors.New("unknown ws type")
|
||||
// }
|
||||
log.Debug(fmt.Sprintf("deleteListenKey resp: %s", string(resp)))
|
||||
case 1:
|
||||
resp, _, err = client.SendFuturesRequestByKey("/fapi/v1/listenKey", "DELETE", nil)
|
||||
log.Debug(fmt.Sprintf("deleteListenKey resp: %s", string(resp)))
|
||||
default:
|
||||
return errors.New("unknown ws type")
|
||||
}
|
||||
|
||||
// return err
|
||||
// }
|
||||
return err
|
||||
}
|
||||
|
||||
func (wm *BinanceWebSocketManager) renewListenKey(listenKey string) error {
|
||||
// payloadParam := map[string]interface{}{
|
||||
// "listenKey": listenKey,
|
||||
// "apiKey": wm.apiKey,
|
||||
// }
|
||||
|
||||
// params := map[string]interface{}{
|
||||
// "id": getUUID(),
|
||||
// "method": "userDataStream.ping",
|
||||
// "params": payloadParam,
|
||||
// }
|
||||
|
||||
// if err := wm.ws.WriteJSON(params); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// wm.ws.WriteJSON()
|
||||
client, err := wm.createBinanceClient()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user