From 32ba6262cceece643caee33409b157c68c45aa51 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Tue, 25 Feb 2025 16:57:59 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/apis/line_coinnetwork.go | 117 +++--- app/admin/apis/line_pre_order.go | 35 +- app/admin/apis/member_withdrawal_log.go | 193 +++++++++ app/admin/fronted/invite_log.go | 46 +++ app/admin/fronted/line_user.go | 43 ++ app/admin/fronted/member_balance_log.go | 42 ++ app/admin/fronted/member_renwal_log.go | 40 +- app/admin/fronted/member_withdrawal_log.go | 106 +++++ app/admin/models/line_coinnetwork.go | 1 + app/admin/models/line_user.go | 1 + app/admin/models/member_balance.go | 1 + app/admin/models/member_balance_log.go | 28 +- app/admin/models/member_renwa_log.go | 36 +- app/admin/models/member_withdrawal_log.go | 40 ++ app/admin/router/invite_log.go | 21 + app/admin/router/line_pre_order.go | 1 + app/admin/router/line_user.go | 4 +- app/admin/router/member_balance_log.go | 15 +- app/admin/router/member_renwa_log.go | 16 +- app/admin/router/member_withdrawal_log.go | 43 ++ app/admin/service/appservice/invite_log.go | 55 +++ app/admin/service/appservice/line_user.go | 205 ++++++++++ .../service/appservice/member_balance_log.go | 44 ++ .../service/appservice/member_renwal_log.go | 143 ++++++- .../appservice/member_withdrawal_log.go | 185 +++++++++ app/admin/service/dto/line_coinnetwork.go | 4 + app/admin/service/dto/line_pre_order.go | 57 +++ app/admin/service/dto/line_user.go | 16 +- app/admin/service/dto/member_balance_log.go | 144 ++++--- app/admin/service/dto/member_renwa_log.go | 77 ++-- .../service/dto/member_withdrawal_log.go | 183 +++++++++ app/admin/service/line_coinnetwork.go | 14 + app/admin/service/line_pre_order.go | 170 +++++++- app/admin/service/member_balance.go | 62 +-- app/admin/service/member_renwa_log.go | 60 +-- app/admin/service/member_withdrawal_log.go | 109 +++++ app/jobs/examples.go | 18 +- app/jobs/jobs.go | 38 +- app/jobs/memberjobs.go | 16 + app/jobs/trxJobs.go | 54 ++- .../member_balance_change_source/dict.go | 7 + .../dicts/member_renwal_log_status/dict.go | 10 + .../member_withdrawal_log_status/dict.go | 16 + common/const/rediskey/redis_key.go | 7 + common/const/sysconfigkey/key.go | 8 + .../sysservice/authservice/authentication.go | 379 +++++++++--------- common/status_code/code_11-12.go | 11 + pkg/cryptohelper/inttostring/Inttoostr.go | 13 + 48 files changed, 2468 insertions(+), 466 deletions(-) create mode 100644 app/admin/apis/member_withdrawal_log.go create mode 100644 app/admin/fronted/invite_log.go create mode 100644 app/admin/fronted/member_balance_log.go create mode 100644 app/admin/fronted/member_withdrawal_log.go create mode 100644 app/admin/models/member_withdrawal_log.go create mode 100644 app/admin/router/invite_log.go create mode 100644 app/admin/router/member_withdrawal_log.go create mode 100644 app/admin/service/appservice/invite_log.go create mode 100644 app/admin/service/appservice/line_user.go create mode 100644 app/admin/service/appservice/member_balance_log.go create mode 100644 app/admin/service/appservice/member_withdrawal_log.go create mode 100644 app/admin/service/dto/member_withdrawal_log.go create mode 100644 app/admin/service/member_withdrawal_log.go create mode 100644 app/jobs/memberjobs.go create mode 100644 common/const/dicts/member_balance_change_source/dict.go create mode 100644 common/const/dicts/member_renwal_log_status/dict.go create mode 100644 common/const/dicts/member_withdrawal_log_status/dict.go create mode 100644 common/const/sysconfigkey/key.go create mode 100644 common/status_code/code_11-12.go diff --git a/app/admin/apis/line_coinnetwork.go b/app/admin/apis/line_coinnetwork.go index 1a85256..c004d1b 100644 --- a/app/admin/apis/line_coinnetwork.go +++ b/app/admin/apis/line_coinnetwork.go @@ -1,9 +1,10 @@ package apis import ( - "fmt" + "fmt" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" "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" @@ -28,18 +29,18 @@ type LineCoinnetwork struct { // @Router /api/v1/line-coinnetwork [get] // @Security Bearer func (e LineCoinnetwork) GetPage(c *gin.Context) { - req := dto.LineCoinnetworkGetPageReq{} - s := service.LineCoinnetwork{} - 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 := dto.LineCoinnetworkGetPageReq{} + s := service.LineCoinnetwork{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req, binding.Form, binding.Query). + 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.LineCoinnetwork, 0) @@ -48,7 +49,7 @@ func (e LineCoinnetwork) GetPage(c *gin.Context) { err = s.GetPage(&req, p, &list, &count) if err != nil { e.Error(500, err, fmt.Sprintf("获取【币种网络】失败,\r\n失败信息 %s", err.Error())) - return + return } e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") @@ -65,7 +66,7 @@ func (e LineCoinnetwork) GetPage(c *gin.Context) { func (e LineCoinnetwork) Get(c *gin.Context) { req := dto.LineCoinnetworkGetReq{} s := service.LineCoinnetwork{} - err := e.MakeContext(c). + err := e.MakeContext(c). MakeOrm(). Bind(&req). MakeService(&s.Service). @@ -81,10 +82,10 @@ func (e LineCoinnetwork) Get(c *gin.Context) { err = s.Get(&req, p, &object) if err != nil { e.Error(500, err, fmt.Sprintf("获取【币种网络】失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( object, "查询成功") + e.OK(object, "查询成功") } // Insert 创建【币种网络】 @@ -98,25 +99,25 @@ func (e LineCoinnetwork) Get(c *gin.Context) { // @Router /api/v1/line-coinnetwork [post] // @Security Bearer func (e LineCoinnetwork) Insert(c *gin.Context) { - req := dto.LineCoinnetworkInsertReq{} - s := service.LineCoinnetwork{} - 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 := dto.LineCoinnetworkInsertReq{} + s := service.LineCoinnetwork{} + 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 + return } e.OK(req.GetId(), "创建成功") @@ -134,27 +135,27 @@ func (e LineCoinnetwork) Insert(c *gin.Context) { // @Router /api/v1/line-coinnetwork/{id} [put] // @Security Bearer func (e LineCoinnetwork) Update(c *gin.Context) { - req := dto.LineCoinnetworkUpdateReq{} - s := service.LineCoinnetwork{} - 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 := dto.LineCoinnetworkUpdateReq{} + s := service.LineCoinnetwork{} + 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 + return } - e.OK( req.GetId(), "修改成功") + e.OK(req.GetId(), "修改成功") } // Delete 删除【币种网络】 @@ -166,18 +167,18 @@ func (e LineCoinnetwork) Update(c *gin.Context) { // @Router /api/v1/line-coinnetwork [delete] // @Security Bearer func (e LineCoinnetwork) Delete(c *gin.Context) { - s := service.LineCoinnetwork{} - req := dto.LineCoinnetworkDeleteReq{} - 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 - } + s := service.LineCoinnetwork{} + req := dto.LineCoinnetworkDeleteReq{} + 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) @@ -185,7 +186,7 @@ func (e LineCoinnetwork) Delete(c *gin.Context) { err = s.Remove(&req, p) if err != nil { e.Error(500, err, fmt.Sprintf("删除【币种网络】失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "删除成功") + e.OK(req.GetId(), "删除成功") } diff --git a/app/admin/apis/line_pre_order.go b/app/admin/apis/line_pre_order.go index 145687a..40a2c1f 100644 --- a/app/admin/apis/line_pre_order.go +++ b/app/admin/apis/line_pre_order.go @@ -113,11 +113,7 @@ func (e LinePreOrder) GetChildOrderList(c *gin.Context) { } p := actions.GetPermissionFromContext(c) list := make([]models.LinePreOrder, 0) - if err != nil { - e.Logger.Error(err) - e.Error(500, err, err.Error()) - return - } + err = s.GetChildList(&req, p, &list) if err != nil { e.Error(500, err, fmt.Sprintf("获取子订单信息失败,\r\n失败信息 %s", err.Error())) @@ -310,6 +306,35 @@ func (e LinePreOrder) AddPreOrder(c *gin.Context) { e.OK(nil, "操作成功") } +// 手动加仓 +func (e LinePreOrder) AddPosition(c *gin.Context) { + s := service.LinePreOrder{} + req := dto.LinePreOrderAddPositionReq{} + 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 + } + + if err := req.Valid(); err != nil { + e.Error(500, err, err.Error()) + return + } + + err = s.AddPosition(&req) + + if err != nil { + e.Error(500, nil, err.Error()) + return + } + e.OK(nil, "操作成功") +} + // BatchAddOrder 批量添加 func (e LinePreOrder) BatchAddOrder(c *gin.Context) { s := service.LinePreOrder{} diff --git a/app/admin/apis/member_withdrawal_log.go b/app/admin/apis/member_withdrawal_log.go new file mode 100644 index 0000000..9ded828 --- /dev/null +++ b/app/admin/apis/member_withdrawal_log.go @@ -0,0 +1,193 @@ +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 MemberWithdrawalLog struct { + api.Api +} + +// GetPage 获取用户提现记录列表 +// @Summary 获取用户提现记录列表 +// @Description 获取用户提现记录列表 +// @Tags 用户提现记录 +// @Param networkName query string false "网络名称" +// @Param status query string false "提现状态(member_withdrawal_status)" +// @Param pageSize query int false "页条数" +// @Param pageIndex query int false "页码" +// @Success 200 {object} response.Response{data=response.Page{list=[]models.MemberWithdrawalLog}} "{"code": 200, "data": [...]}" +// @Router /api/v1/member-withdrawal-log [get] +// @Security Bearer +func (e MemberWithdrawalLog) GetPage(c *gin.Context) { + req := dto.MemberWithdrawalLogGetPageReq{} + s := service.MemberWithdrawalLog{} + 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.MemberWithdrawalLog, 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.MemberWithdrawalLog} "{"code": 200, "data": [...]}" +// @Router /api/v1/member-withdrawal-log/{id} [get] +// @Security Bearer +func (e MemberWithdrawalLog) Get(c *gin.Context) { + req := dto.MemberWithdrawalLogGetReq{} + s := service.MemberWithdrawalLog{} + 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.MemberWithdrawalLog + + 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.MemberWithdrawalLogInsertReq true "data" +// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}" +// @Router /api/v1/member-withdrawal-log [post] +// @Security Bearer +func (e MemberWithdrawalLog) Insert(c *gin.Context) { + req := dto.MemberWithdrawalLogInsertReq{} + s := service.MemberWithdrawalLog{} + 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.MemberWithdrawalLogUpdateReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}" +// @Router /api/v1/member-withdrawal-log/{id} [put] +// @Security Bearer +func (e MemberWithdrawalLog) Update(c *gin.Context) { + req := dto.MemberWithdrawalLogUpdateReq{} + s := service.MemberWithdrawalLog{} + 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.MemberWithdrawalLogDeleteReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}" +// @Router /api/v1/member-withdrawal-log [delete] +// @Security Bearer +func (e MemberWithdrawalLog) Delete(c *gin.Context) { + s := service.MemberWithdrawalLog{} + req := dto.MemberWithdrawalLogDeleteReq{} + 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(), "删除成功") +} diff --git a/app/admin/fronted/invite_log.go b/app/admin/fronted/invite_log.go new file mode 100644 index 0000000..c049969 --- /dev/null +++ b/app/admin/fronted/invite_log.go @@ -0,0 +1,46 @@ +package fronted + +import ( + "go-admin/app/admin/service/appservice" + "go-admin/app/admin/service/common" + "go-admin/app/admin/service/dto" + + "github.com/gin-gonic/gin" + "github.com/go-admin-team/go-admin-core/sdk/api" +) + +type InviteLog struct { + api.Api +} + +// GetPage 获取InviteLog列表 +func (e InviteLog) GetPersonnalPage(c *gin.Context) { + s := appservice.InviteLog{} + req := dto.PersonnalInviteLogPageReq{} + 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 + } + + datas := make([]dto.InviteLogResp, 0) + req.UserId = common.GetUserId(c) + + if req.UserId == 0 { + req.UserId = -1 + } + var count int64 + err = s.GetPage(&req, &datas, &count) + + if err != nil { + e.Error(500, err, err.Error()) + return + } + + e.PageOK(datas, int(count), req.PageIndex, req.PageSize, "success") +} diff --git a/app/admin/fronted/line_user.go b/app/admin/fronted/line_user.go index 8cc610a..e15e0c3 100644 --- a/app/admin/fronted/line_user.go +++ b/app/admin/fronted/line_user.go @@ -7,6 +7,7 @@ import ( "go-admin/app/admin/models/sysmodel" "go-admin/app/admin/service" "go-admin/app/admin/service/aduserdb" + "go-admin/app/admin/service/appservice" "go-admin/app/admin/service/common" "go-admin/app/admin/service/dto" "go-admin/common/const/rediskey" @@ -23,6 +24,7 @@ import ( "github.com/bytedance/sonic" "github.com/gin-gonic/gin" + "github.com/go-admin-team/go-admin-core/logger" "github.com/go-admin-team/go-admin-core/sdk/api" "github.com/shopspring/decimal" "gorm.io/gorm" @@ -35,11 +37,15 @@ type LineUserApi struct { // Register 用户注册 func (e LineUserApi) Register(c *gin.Context) { s := service.LineUser{} + userAppService := appservice.LineUser{} + balanceService := service.MemberBalance{} req := sysmodel.FrontedUserRegisterReq{} err := e.MakeContext(c). MakeOrm(). Bind(&req). MakeService(&s.Service). + MakeService(&userAppService.Service). + MakeService(&balanceService.Service). Errors if err != nil { e.Logger.Error(err) @@ -66,6 +72,10 @@ func (e LineUserApi) Register(c *gin.Context) { e.Error(ok, nil, sysstatuscode.GetStatusCodeDescription(c, ok)) return } + + userAppService.SetDefaultRenwal(user) + balanceService.CreateDefaultBalance(user) + resp := map[string]interface{}{ "email": req.Email, "mobile": req.Phone, @@ -579,3 +589,36 @@ func (e LineUserApi) CallBack(c *gin.Context) { e.OK(nil, "") } + +// 退出登录 +func (e LineUserApi) Logout(c *gin.Context) { + err := e.MakeContext(c). + MakeOrm(). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + + userId := common.GetUserId(c) + err = authservice.Logout(userId, 1) + + if err != nil { + logger.Error("退出登录失败:", err.Error()) + e.Error(statuscode.ServerError, nil, "") + } + + e.OK(nil, "success") +} + +// 查询个人资产 +func (e LineUserApi) GetProperty(c *gin.Context) { + err := e.MakeContext(c). + MakeOrm(). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(statuscode.ServerError, nil, sysstatuscode.GetStatusCodeDescription(c, statuscode.ServerError)) + } +} diff --git a/app/admin/fronted/member_balance_log.go b/app/admin/fronted/member_balance_log.go new file mode 100644 index 0000000..76fa1a3 --- /dev/null +++ b/app/admin/fronted/member_balance_log.go @@ -0,0 +1,42 @@ +package fronted + +import ( + "go-admin/app/admin/service/appservice" + "go-admin/app/admin/service/common" + "go-admin/app/admin/service/dto" + "go-admin/common/service/sysservice/sysstatuscode" + statuscode "go-admin/common/status_code" + + "github.com/gin-gonic/gin" + "github.com/go-admin-team/go-admin-core/sdk/api" +) + +type MemberBalanceLog struct { + api.Api +} + +// 分页查询 +func (e MemberBalanceLog) GetPage(c *gin.Context) { + s := appservice.MemberBalanceLog{} + req := dto.MemberBalanceLogPageAppReq{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, "server error") + return + } + req.UserId = common.GetUserId(c) + data := make([]dto.MemberBalanceLogAppResp, 0) + var count int64 + code := s.GetPage(&req, &data, &count) + + if code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + } + + e.PageOK(data, int(count), req.PageIndex, req.PageSize, "success") +} diff --git a/app/admin/fronted/member_renwal_log.go b/app/admin/fronted/member_renwal_log.go index c9fe881..5b78d62 100644 --- a/app/admin/fronted/member_renwal_log.go +++ b/app/admin/fronted/member_renwal_log.go @@ -19,14 +19,50 @@ type MemberRenwalLog struct { func (e MemberRenwalLog) GetPage(c *gin.Context) { s := appservice.MemberRenwalLog{} req := dto.MemberRenwalLogPageAppReq{} - + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, "server error") + return + } userId := common.GetUserId(c) data := make([]dto.MemberRenwalLogResp, 0) - code := s.GetPage(userId, &req, &data) + var count int64 + code := s.GetPage(userId, &req, &data, &count) if code != statuscode.OK { e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) } + e.PageOK(data, int(count), req.PageIndex, req.PageSize, "success") +} + +func (e MemberRenwalLog) Apply(c *gin.Context) { + s := appservice.MemberRenwalLog{} + req := dto.MemberRenwalCreateAppReq{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, "server error") + return + } + + req.UserId = common.GetUserId(c) + data := dto.MemberRenwalCreateAppResp{} + code := s.Renwal(&req, &data) + + if code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return + } + e.OK(data, "success") } diff --git a/app/admin/fronted/member_withdrawal_log.go b/app/admin/fronted/member_withdrawal_log.go new file mode 100644 index 0000000..3542270 --- /dev/null +++ b/app/admin/fronted/member_withdrawal_log.go @@ -0,0 +1,106 @@ +package fronted + +import ( + "go-admin/app/admin/service/appservice" + "go-admin/app/admin/service/common" + "go-admin/app/admin/service/dto" + "go-admin/common/service/sysservice/sysstatuscode" + statuscode "go-admin/common/status_code" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "github.com/go-admin-team/go-admin-core/sdk/api" +) + +type MemberWithdrawalLog struct { + api.Api +} + +// 分页查询 +func (e MemberWithdrawalLog) GetPage(c *gin.Context) { + s := appservice.MemberWithdrawalLog{} + req := dto.MemberWithdrawalLogGetPageAppReq{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req, binding.Form, binding.Query). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + + var count int64 + var list []dto.MemberWithdrawalLogResp + + code := s.GetPage(&req, &list, &count) + + if code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + } + + e.PageOK(list, int(count), req.PageIndex, req.PageSize, "success") +} + +// 用户提现申请 +func (e MemberWithdrawalLog) Apply(c *gin.Context) { + s := appservice.MemberWithdrawalLog{} + req := dto.MemberWithdrawalLogApplyReq{} + 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 + } + + if code := req.Valid(); code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return + } + + req.UserId = common.GetUserId(c) + code := s.Apply(&req) + + if code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return + } + + e.OK(nil, "success") +} + +// 用户取消提现 +func (e MemberWithdrawalLog) Cancel(c *gin.Context) { + s := appservice.MemberWithdrawalLog{} + req := dto.MemberWithdrawalLogCancelReq{} + 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 + } + + if code := req.Valid(); code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return + } + + userId := common.GetUserId(c) + code := s.Canceled(&req, userId) + + if code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return + } + + e.OK(nil, "success") +} diff --git a/app/admin/models/line_coinnetwork.go b/app/admin/models/line_coinnetwork.go index 124cff6..e9c4116 100644 --- a/app/admin/models/line_coinnetwork.go +++ b/app/admin/models/line_coinnetwork.go @@ -16,6 +16,7 @@ type LineCoinnetwork struct { UnlockTime int64 `json:"unlockTime" gorm:"type:int;comment:提现确认平均时间,单位分钟"` Fee decimal.Decimal `json:"fee" gorm:"type:decimal(32,6);comment:网络手续费(百分比)"` MinWithdrawal decimal.Decimal `json:"minWithdrawal" gorm:"type:decimal(32,6);comment:最小提现金额"` + Status int `json:"status" gorm:"type:int;comment:状态 1 启用 2 禁用"` models.ModelTime models.ControlBy } diff --git a/app/admin/models/line_user.go b/app/admin/models/line_user.go index 66e7e1a..e1e9492 100644 --- a/app/admin/models/line_user.go +++ b/app/admin/models/line_user.go @@ -39,6 +39,7 @@ type LineUser struct { Status string `json:"status" gorm:"type:varchar(30);comment:状态"` Verification string `json:"verification" gorm:"type:varchar(255);comment:验证"` LoginTime time.Time `json:"loginTime" gorm:"type:timestamp;comment:登录时间"` + ExpirationTime *time.Time `json:"expirationTime" gorm:"type:timestamp;comment:过期时间"` OpenStatus int `json:"open_status" gorm:"-"` models.ModelTime models.ControlBy diff --git a/app/admin/models/member_balance.go b/app/admin/models/member_balance.go index 964cc67..0a53119 100644 --- a/app/admin/models/member_balance.go +++ b/app/admin/models/member_balance.go @@ -9,6 +9,7 @@ import ( type MemberBalance struct { models.Model + UserId int `json:"userId" gorm:"type:bigint;comment:用户id"` TotalAmont decimal.Decimal `json:"totalAmont" gorm:"type:decimal(18,6);comment:总余额"` FreeAmount decimal.Decimal `json:"freeAmount" gorm:"type:decimal(18,6);comment:可用余额"` FrozenAmount decimal.Decimal `json:"frozenAmount" gorm:"type:decimal(18,6);comment:冻结金额"` diff --git a/app/admin/models/member_balance_log.go b/app/admin/models/member_balance_log.go index 5a30605..c74f41f 100644 --- a/app/admin/models/member_balance_log.go +++ b/app/admin/models/member_balance_log.go @@ -1,27 +1,27 @@ package models import ( - "go-admin/common/models" + "github.com/shopspring/decimal" ) type MemberBalanceLog struct { - models.Model - - UserId int `json:"userId" gorm:"type:bigint;comment:用户id"` - ChangeSource string `json:"changeSource" gorm:"type:varchar(50);comment:变更来源 (member_change_source)"` - ChangeType string `json:"changeType" gorm:"type:tinyint;comment:变更类别 1-收入 2-支出"` - Amount string `json:"amount" gorm:"type:decimal(18,6);comment:变更金额"` - BalanceBefore string `json:"balanceBefore" gorm:"type:decimal(18,6);comment:变更前余额"` - BalanceAfter string `json:"balanceAfter" gorm:"type:decimal(18,6);comment:变更后余额"` - Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"` - models.ModelTime - models.ControlBy + models.Model + + UserId int `json:"userId" gorm:"type:bigint;comment:用户id"` + ChangeSource string `json:"changeSource" gorm:"type:varchar(50);comment:变更来源 (member_change_source)"` + ChangeType int `json:"changeType" gorm:"type:tinyint;comment:变更类别 1-收入 2-支出"` + Amount decimal.Decimal `json:"amount" gorm:"type:decimal(18,6);comment:变更金额"` + BalanceBefore decimal.Decimal `json:"balanceBefore" gorm:"type:decimal(18,6);comment:变更前余额"` + BalanceAfter decimal.Decimal `json:"balanceAfter" gorm:"type:decimal(18,6);comment:变更后余额"` + Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"` + models.ModelTime + models.ControlBy } func (MemberBalanceLog) TableName() string { - return "member_balance_log" + return "member_balance_log" } func (e *MemberBalanceLog) Generate() models.ActiveRecord { @@ -31,4 +31,4 @@ func (e *MemberBalanceLog) Generate() models.ActiveRecord { func (e *MemberBalanceLog) GetId() interface{} { return e.Id -} \ No newline at end of file +} diff --git a/app/admin/models/member_renwa_log.go b/app/admin/models/member_renwa_log.go index fcc2b63..b4b20d7 100644 --- a/app/admin/models/member_renwa_log.go +++ b/app/admin/models/member_renwa_log.go @@ -1,30 +1,34 @@ package models import ( - "time" + "time" "go-admin/common/models" + "github.com/shopspring/decimal" ) type MemberRenwaLog struct { - models.Model - - RenwalId string `json:"renwalId" gorm:"type:bigint;comment:套餐id"` - RenwalName string `json:"renwalName" gorm:"type:varchar(255);comment:续期套餐名称"` - RenwalDuration string `json:"renwalDuration" gorm:"type:int;comment:续期时长(天数)"` - Status string `json:"status" gorm:"type:varchar(255);comment:订单状态(member_renwal_log_status)"` - PayableAmount string `json:"payableAmount" gorm:"type:decimal(18,6);comment:应付金额"` - ActualPaymentAmount string `json:"actualPaymentAmount" gorm:"type:decimal(18,6);comment:实付金额"` - FromAddress string `json:"fromAddress" gorm:"type:varchar(255);comment:付款地址"` - Coin string `json:"coin" gorm:"type:varchar(255);comment:代币"` - PaymentTime time.Time `json:"paymentTime" gorm:"type:datetime;comment:支付时间"` - models.ModelTime - models.ControlBy + models.Model + + RenwalId int `json:"renwalId" gorm:"type:bigint;comment:套餐id"` + UserId int `json:"userId" gorm:"type:bigint;comment:用户id"` + RenwalName string `json:"renwalName" gorm:"type:varchar(255);comment:续期套餐名称"` + RenwalDuration int `json:"renwalDuration" gorm:"type:int;comment:续期时长(天数)"` + Status string `json:"status" gorm:"type:varchar(255);comment:订单状态(member_renwal_log_status)"` + PayableAmount decimal.Decimal `json:"payableAmount" gorm:"type:decimal(18,6);comment:应付金额"` + ActualPaymentAmount decimal.Decimal `json:"actualPaymentAmount" gorm:"type:decimal(18,6);comment:实付金额"` + FromAddress string `json:"fromAddress" gorm:"type:varchar(255);comment:付款地址"` + Coin string `json:"coin" gorm:"type:varchar(255);comment:代币"` + PaymentTime *time.Time `json:"paymentTime" gorm:"type:datetime;comment:支付时间"` + Hash string `json:"hash" gorm:"type:varchar(255);comment:交易hash"` + ExpirationTime time.Time `json:"expirationTime" gorm:"type:datetime;comment:到期时间"` + models.ModelTime + models.ControlBy } func (MemberRenwaLog) TableName() string { - return "member_renwa_log" + return "member_renwa_log" } func (e *MemberRenwaLog) Generate() models.ActiveRecord { @@ -34,4 +38,4 @@ func (e *MemberRenwaLog) Generate() models.ActiveRecord { func (e *MemberRenwaLog) GetId() interface{} { return e.Id -} \ No newline at end of file +} diff --git a/app/admin/models/member_withdrawal_log.go b/app/admin/models/member_withdrawal_log.go new file mode 100644 index 0000000..3165ef5 --- /dev/null +++ b/app/admin/models/member_withdrawal_log.go @@ -0,0 +1,40 @@ +package models + +import ( + "time" + + "go-admin/common/models" + + "github.com/shopspring/decimal" +) + +type MemberWithdrawalLog struct { + models.Model + + NetworkId int `json:"networkId" gorm:"type:int;comment:网络id"` + NetworkName string `json:"networkName" gorm:"type:varchar(50);comment:网络名称"` + Coin string `json:"coin" gorm:"type:varchar(50);comment:提现币种"` + ToAddress string `json:"toAddress" gorm:"type:varchar(255);comment:提现地址"` + Hash string `json:"hash" gorm:"type:varchar(255);comment:提现hash"` + UserId int `json:"userId" gorm:"type:bigint;comment:用户id"` + Amount decimal.Decimal `json:"amount" gorm:"type:decimal(32,6);comment:提现金额(U)"` + Status string `json:"status" gorm:"type:varchar(20);comment:提现状态(member_withdrawal_status)"` + ConfirmTime *time.Time `json:"confirmTime" gorm:"type:datetime;comment:确认时间"` + Fee decimal.Decimal `json:"fee" gorm:"type:decimal(10,2);comment:手续费比例"` + Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"` + models.ModelTime + models.ControlBy +} + +func (MemberWithdrawalLog) TableName() string { + return "member_withdrawal_log" +} + +func (e *MemberWithdrawalLog) Generate() models.ActiveRecord { + o := *e + return &o +} + +func (e *MemberWithdrawalLog) GetId() interface{} { + return e.Id +} diff --git a/app/admin/router/invite_log.go b/app/admin/router/invite_log.go new file mode 100644 index 0000000..6c522d7 --- /dev/null +++ b/app/admin/router/invite_log.go @@ -0,0 +1,21 @@ +package router + +import ( + "go-admin/app/admin/fronted" + "go-admin/common/middleware" + + "github.com/gin-gonic/gin" +) + +func init() { + routerFrontedCheckRole = append(routerFrontedCheckRole, registerInviteLogFrontedRouter) +} + +// registerLineApiGroupRouter +func registerInviteLogFrontedRouter(v1 *gin.RouterGroup) { + api := fronted.InviteLog{} + r := v1.Group("/invite-log") + { + r.GET("", middleware.FrontedAuth, api.GetPersonnalPage) + } +} diff --git a/app/admin/router/line_pre_order.go b/app/admin/router/line_pre_order.go index 0dfca73..d1c759f 100644 --- a/app/admin/router/line_pre_order.go +++ b/app/admin/router/line_pre_order.go @@ -26,6 +26,7 @@ func registerLinePreOrderRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTM r.DELETE("", api.Delete) r.POST("addOrder", actions.PermissionAction(), api.AddPreOrder) //添加订单 + r.POST("position", actions.PermissionAction(), api.AddPosition) //手动加仓 r.POST("batchAddOrder", actions.PermissionAction(), api.BatchAddOrder) //批量添加订单 r.POST("quickAddPreOrder", actions.PermissionAction(), api.QuickAddPreOrder) //快捷下单 r.POST("lever", actions.PermissionAction(), api.Lever) //设置杠杆 diff --git a/app/admin/router/line_user.go b/app/admin/router/line_user.go index 35cafa0..bd02ff3 100644 --- a/app/admin/router/line_user.go +++ b/app/admin/router/line_user.go @@ -1,9 +1,10 @@ package router import ( + "go-admin/app/admin/fronted" + "github.com/gin-gonic/gin" jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" - "go-admin/app/admin/fronted" "go-admin/app/admin/apis" "go-admin/common/actions" @@ -45,6 +46,7 @@ func frontedRegisterLinUserRouter(v1 *gin.RouterGroup) { r.POST("/addApiAuth", middleware.FrontedAuth, api.AddApiKey) //用户手动添加Apikey r.POST("/updateApiAuth", middleware.FrontedAuth, api.UpdateApiKey) //用户手动修改Apikey r.POST("/opStatus", middleware.FrontedAuth, api.OpenStatus) //开启或者关闭状态 + r.DELETE("/logout", middleware.FrontedAuth, api.Logout) //退出登录 //充值 r.POST("/notify", api.Notify) //uDun回调 diff --git a/app/admin/router/member_balance_log.go b/app/admin/router/member_balance_log.go index a07ca64..6bf2a15 100644 --- a/app/admin/router/member_balance_log.go +++ b/app/admin/router/member_balance_log.go @@ -5,12 +5,14 @@ import ( jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" "go-admin/app/admin/apis" - "go-admin/common/middleware" + "go-admin/app/admin/fronted" "go-admin/common/actions" + "go-admin/common/middleware" ) func init() { routerCheckRole = append(routerCheckRole, registerMemberBalanceLogRouter) + routerFrontedCheckRole = append(routerFrontedCheckRole, registerMemberBalanceLogFrontedRouter) } // registerMemberBalanceLogRouter @@ -24,4 +26,13 @@ func registerMemberBalanceLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.Gin r.PUT("/:id", actions.PermissionAction(), api.Update) r.DELETE("", api.Delete) } -} \ No newline at end of file +} + +func registerMemberBalanceLogFrontedRouter(v1 *gin.RouterGroup) { + api := fronted.MemberBalanceLog{} + + r := v1.Group("/member-balance-log", middleware.FrontedAuth) + { + r.GET("/personal", api.GetPage) //app 个人余额明细 + } +} diff --git a/app/admin/router/member_renwa_log.go b/app/admin/router/member_renwa_log.go index b582086..10064ab 100644 --- a/app/admin/router/member_renwa_log.go +++ b/app/admin/router/member_renwa_log.go @@ -5,12 +5,14 @@ import ( jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" "go-admin/app/admin/apis" - "go-admin/common/middleware" + "go-admin/app/admin/fronted" "go-admin/common/actions" + "go-admin/common/middleware" ) func init() { routerCheckRole = append(routerCheckRole, registerMemberRenwaLogRouter) + routerFrontedCheckRole = append(routerFrontedCheckRole, registerMemberRenwaLogFrontedRouter) } // registerMemberRenwaLogRouter @@ -24,4 +26,14 @@ func registerMemberRenwaLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJW r.PUT("/:id", actions.PermissionAction(), api.Update) r.DELETE("", api.Delete) } -} \ No newline at end of file +} + +func registerMemberRenwaLogFrontedRouter(v1 *gin.RouterGroup) { + api := fronted.MemberRenwalLog{} + + f := v1.Group("/member-renwal", middleware.FrontedAuth) + { + f.GET("/list", api.GetPage) + f.POST("/apply", api.Apply) + } +} diff --git a/app/admin/router/member_withdrawal_log.go b/app/admin/router/member_withdrawal_log.go new file mode 100644 index 0000000..c5bfde8 --- /dev/null +++ b/app/admin/router/member_withdrawal_log.go @@ -0,0 +1,43 @@ +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/app/admin/fronted" + "go-admin/common/actions" + "go-admin/common/middleware" +) + +func init() { + routerCheckRole = append(routerCheckRole, registerMemberWithdrawalLogRouter) + routerFrontedCheckRole = append(routerFrontedCheckRole, registerMemberWidthdrawalLogFrontedRouter) +} + +// registerMemberWithdrawalLogRouter +func registerMemberWithdrawalLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { + api := apis.MemberWithdrawalLog{} + r := v1.Group("/member-withdrawal-log").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) + } +} + +func registerMemberWidthdrawalLogFrontedRouter(v1 *gin.RouterGroup) { + api := fronted.MemberWithdrawalLog{} + + r := v1.Group("/member-withdrawal", middleware.FrontedAuth) + { + r.GET("", api.GetPage) + // r.GET("/:id", api.Get) + r.POST("", api.Apply) + r.PUT("/cancel", api.Cancel) + // r.PUT("/:id", api.Update) + // r.DELETE("", api.Delete) + } +} diff --git a/app/admin/service/appservice/invite_log.go b/app/admin/service/appservice/invite_log.go new file mode 100644 index 0000000..2eb8935 --- /dev/null +++ b/app/admin/service/appservice/invite_log.go @@ -0,0 +1,55 @@ +package appservice + +import ( + "go-admin/app/admin/models" + "go-admin/app/admin/service/dto" + cDto "go-admin/common/dto" + "time" + + "github.com/go-admin-team/go-admin-core/logger" + "github.com/go-admin-team/go-admin-core/sdk/service" +) + +type InviteLog struct { + service.Service +} + +// 分页查询 +func (e *InviteLog) GetPage(req *dto.PersonnalInviteLogPageReq, list *[]dto.InviteLogResp, count *int64) error { + var datas []models.LineUser + var childs []models.LineUser + pids := make([]int, 0) + + if err := e.Orm.Model(&models.LineUser{}).Where("pid = ?", req.UserId).Scopes(cDto.Paginate(req.GetPageSize(), req.GetPageIndex())).Find(&datas).Count(count).Error; err != nil { + return err + } + + for _, v := range datas { + pids = append(pids, v.Pid) + } + if err := e.Orm.Model(&models.LineUser{}).Where("pid in (?)", pids).Find(&childs).Error; err != nil { + logger.Error("查询子邀请失败", err) + } + + for _, v := range datas { + item := dto.InviteLogResp{ + UserId: v.Id, + NickName: v.Username, + RegisterTime: v.CreatedAt.UnixNano() / int64(time.Millisecond), + } + + for _, v2 := range childs { + if v2.Pid == v.Id { + item.Childs = append(item.Childs, dto.InviteLogResp{ + UserId: v2.Id, + NickName: v2.Nickname, + RegisterTime: v2.CreatedAt.UnixNano() / int64(time.Millisecond), + }) + } + } + + *list = append(*list, item) + } + + return nil +} diff --git a/app/admin/service/appservice/line_user.go b/app/admin/service/appservice/line_user.go new file mode 100644 index 0000000..7f2b0f8 --- /dev/null +++ b/app/admin/service/appservice/line_user.go @@ -0,0 +1,205 @@ +package appservice + +import ( + "errors" + "fmt" + "go-admin/app/admin/models" + adminservice "go-admin/app/admin/service" + "go-admin/app/admin/service/dto" + memberbalancechangesource "go-admin/common/const/dicts/member_balance_change_source" + memberrenwalconfigstatus "go-admin/common/const/dicts/member_renwal_config_status" + memberrenwallogstatus "go-admin/common/const/dicts/member_renwal_log_status" + "go-admin/common/const/rediskey" + "go-admin/common/helper" + "go-admin/pkg/utility" + "time" + + "github.com/go-admin-team/go-admin-core/logger" + "github.com/go-admin-team/go-admin-core/sdk/service" + "github.com/shopspring/decimal" + "gorm.io/gorm" +) + +type LineUser struct { + service.Service +} + +// 注册赠送默认套餐 +func (e *LineUser) SetDefaultRenwal(user *models.LineUser) error { + var defaultRenwalConfig models.MemberRenwalConfig + + if err := e.Orm.Model(&defaultRenwalConfig).Where("is_default =1 AND status =?", memberrenwalconfigstatus.ENABLE).First(&defaultRenwalConfig).Error; err != nil { + + logger.Error("没有默认续费配置 err", err) + return nil + } + + now := time.Now() + //续费时长 + if defaultRenwalConfig.DurationDay > 0 { + renwalLog := models.MemberRenwaLog{ + RenwalId: defaultRenwalConfig.Id, + RenwalName: defaultRenwalConfig.PackageName, + UserId: user.Id, + RenwalDuration: defaultRenwalConfig.DurationDay, + Status: memberrenwallogstatus.PENDING, + PayableAmount: defaultRenwalConfig.OriginalPrice, + ActualPaymentAmount: decimal.Zero, + PaymentTime: &now, + } + + err := e.Orm.Transaction(func(tx *gorm.DB) error { + expirationTime := time.Now().Add(time.Hour * 24 * time.Duration(defaultRenwalConfig.DurationDay)) + user.ExpirationTime = &expirationTime + + if err := tx.Model(&user).Update("expiration_time", expirationTime).Error; err != nil { + logger.Errorf("默认套餐修改过期时间失败 userid:%v err:%v", user.Id, err) + return err + } + + if err := tx.Create(&renwalLog).Error; err != nil { + logger.Errorf("新增续费记录失败 userid:%v err:%v", user.Id, err) + return err + } + + return nil + }) + + if err != nil { + logger.Error("续费失败 err", err) + } + } + + return nil +} + +// 处理过期账户 +func (e *LineUser) Expire() error { + err := e.Orm.Model(&models.LineUser{}).Where("expiration_time ? AND status =?", data.PayableAmount, startTime, memberrenwallogstatus.PENDING).First(&log).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + // 如果记录未找到,跳过该项 + logger.Warnf("续费记录未找到 hash:%v amount:%v", data.Hash, data.PayableAmount) + continue + } + logger.Errorf("续费记录查询失败 hash:%v amount:%v err:%v", data.Hash, data.PayableAmount, err) + continue + } + + log.Status = memberrenwallogstatus.PAID + log.PaymentTime = &now + log.FromAddress = data.FromAddress + log.Hash = data.Hash + // 查询用户信息 + user := models.LineUser{} + if err := e.Orm.Model(&user).Where("id =?", log.UserId).First(&user).Error; err != nil { + logger.Errorf("用户查询失败 hash:%v amount:%v err:%v", data.Hash, data.PayableAmount, err) + continue + } + + // 更新 ExpirationTime + if user.ExpirationTime != nil && user.ExpirationTime.After(now) { + expirationTime := user.ExpirationTime.AddDate(0, 0, log.RenwalDuration) + user.ExpirationTime = &expirationTime + } else { + expirationTime := now.AddDate(0, 0, log.RenwalDuration) + user.ExpirationTime = &expirationTime + } + + // 计算返现记录 + balanceLogs := make([]models.MemberBalanceLog, 0) + if user.Pid > 0 { + if set, ok := configs["member_invitation_rate1"]; ok { + rate1 := utility.StrToDecimal(set.ConfigValue) + if rate1.Cmp(decimal.Zero) > 0 { + balanceLogs = append(balanceLogs, calculateBalance(user.Pid, data.PayableAmount, rate1)) + } + } + } + + if user.TopReferrerId > 0 { + if set, ok := configs["member_invitation_rate2"]; ok { + rate1 := utility.StrToDecimal(set.ConfigValue) + if rate1.Cmp(decimal.Zero) > 0 { + balanceLogs = append(balanceLogs, calculateBalance(user.TopReferrerId, data.PayableAmount, rate1)) + } + } + } + + // 修改续期订单记录 + if err := e.Orm.Model(log).Save(&log).Error; err != nil { + logger.Errorf("续费更新订单失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err) + continue + } + // 更新用户信息 + if err := e.Orm.Model(&user).Updates(user).Error; err != nil { + logger.Errorf("续费更新失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err) + continue + } + + // 事务操作,保存返现记录及更新余额 + err := e.Orm.Transaction(func(tx *gorm.DB) error { + // 保存返现记录 + if err := e.Orm.Model(&models.MemberBalanceLog{}).Create(&balanceLogs).Error; err != nil { + logger.Errorf("续费保存返现记录失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err) + return err + } + + // 更新用户余额 + for _, item := range balanceLogs { + if err := e.Orm.Exec("update member_balance set total_amount=total_amount+?,free_amount=free_amount +? where user_id=?", item.Amount, item.UserId).Error; err != nil { + logger.Errorf("续费更新余额失败 userid:%v 返现userid:%v hash:%v amount:%v err:%v", user.Id, item.UserId, data.Hash, data.PayableAmount, err) + return err + } + } + + return nil + }) + + // 事务失败处理 + if err != nil { + logger.Errorf("续费成功,返现失败:续费userid:%v,hash:%s,err:%v", user.Id, data.Hash, err) + continue + } + + if err := helper.DefaultRedis.SetString(hashKey, "1"); err != nil { + logger.Errorf("续费成功,写入hash缓存失败 :续费userid:%v,hash:%s,err:%v", user.Id, data.Hash, err) + } + } + + return nil +} + +// 计算返现的函数 +func calculateBalance(userId int, amount decimal.Decimal, rate decimal.Decimal) models.MemberBalanceLog { + return models.MemberBalanceLog{ + UserId: userId, + ChangeSource: memberbalancechangesource.WITH_DRAW, + ChangeType: 1, + Amount: amount.Mul(rate.Div(decimal.NewFromInt(100))).Truncate(2), + } +} diff --git a/app/admin/service/appservice/member_balance_log.go b/app/admin/service/appservice/member_balance_log.go new file mode 100644 index 0000000..1f60f5a --- /dev/null +++ b/app/admin/service/appservice/member_balance_log.go @@ -0,0 +1,44 @@ +package appservice + +import ( + "go-admin/app/admin/models" + "go-admin/app/admin/service/dto" + cDto "go-admin/common/dto" + statuscode "go-admin/common/status_code" + "time" + + "github.com/go-admin-team/go-admin-core/sdk/service" + "github.com/jinzhu/copier" +) + +type MemberBalanceLog struct { + service.Service +} + +// 分页查询个人资金记录 +func (e *MemberBalanceLog) GetPage(req *dto.MemberBalanceLogPageAppReq, list *[]dto.MemberBalanceLogAppResp, count *int64) int { + var data models.MemberBalanceLog + var datas []models.MemberBalanceLog + item := dto.MemberBalanceLogAppResp{} + + err := e.Orm.Model(&data). + Where("user_id = ?", req.UserId). + Scopes( + cDto.Paginate(req.GetPageSize(), req.GetPageIndex()), + ). + Find(&datas).Limit(-1).Offset(-1). + Count(count).Error + + if err != nil { + return statuscode.ServerError + } + + for _, v := range datas { + copier.Copy(&item, v) + + item.CreateTimeUnix = v.CreatedAt.UnixNano() / int64(time.Millisecond) + *list = append(*list, item) + } + + return statuscode.OK +} diff --git a/app/admin/service/appservice/member_renwal_log.go b/app/admin/service/appservice/member_renwal_log.go index 52111b1..5b8ab3f 100644 --- a/app/admin/service/appservice/member_renwal_log.go +++ b/app/admin/service/appservice/member_renwal_log.go @@ -1,13 +1,26 @@ package appservice import ( + "errors" + "fmt" "go-admin/app/admin/models" + adminservice "go-admin/app/admin/service" "go-admin/app/admin/service/dto" + memberrenwalconfigstatus "go-admin/common/const/dicts/member_renwal_config_status" + memberrenwallogstatus "go-admin/common/const/dicts/member_renwal_log_status" + "go-admin/common/const/rediskey" cDto "go-admin/common/dto" + "go-admin/common/helper" statuscode "go-admin/common/status_code" + "go-admin/pkg/utility" + "math/rand" + "time" + "github.com/go-admin-team/go-admin-core/logger" "github.com/go-admin-team/go-admin-core/sdk/service" "github.com/jinzhu/copier" + "github.com/shopspring/decimal" + "gorm.io/gorm" ) type MemberRenwalLog struct { @@ -15,9 +28,9 @@ type MemberRenwalLog struct { } // 分页查询续期记录 -func (e *MemberRenwalLog) GetPage(userId int, req *dto.MemberRenwalLogPageAppReq, list *[]dto.MemberRenwalLogResp) int { - var count int64 +func (e *MemberRenwalLog) GetPage(userId int, req *dto.MemberRenwalLogPageAppReq, list *[]dto.MemberRenwalLogResp, count *int64) int { var data models.MemberRenwaLog + var datas []models.MemberRenwaLog resp := dto.MemberRenwalLogResp{} err := e.Orm.Model(&data). @@ -25,18 +38,138 @@ func (e *MemberRenwalLog) GetPage(userId int, req *dto.MemberRenwalLogPageAppReq Scopes( cDto.Paginate(req.GetPageSize(), req.GetPageIndex()), ). - Find(list).Limit(-1).Offset(-1). - Count(&count).Error + Find(&datas).Limit(-1).Offset(-1). + Count(count).Error if err != nil { return statuscode.ServerError } - for _, item := range *list { + for _, item := range datas { copier.Copy(&resp, item) + if item.PaymentTime != nil { + resp.PaymentTimeUnix = item.PaymentTime.UnixNano() / int64(time.Millisecond) + } + + resp.ExpirationTimeUnix = item.ExpirationTime.UnixNano() / int64(time.Millisecond) + *list = append(*list, resp) } return statuscode.OK } + +// 续期订单 +func (e *MemberRenwalLog) Renwal(req *dto.MemberRenwalCreateAppReq, data *dto.MemberRenwalCreateAppResp) int { + var renwalConfig models.MemberRenwalConfig + renwalLog := models.MemberRenwaLog{} + now := time.Now() + configService := adminservice.SysConfig{Service: e.Service} + configDict := map[string]dto.GetSysConfigByKEYForServiceResp{} + expirationVal := 30 + var toAddress string + + configService.GetWithKeys(&[]string{"member_renwal_expiration_val", "member_receive_address"}, &configDict) + + if dict, ok := configDict["member_renwal_expiration_val"]; ok && dict.ConfigValue != "" { + expirationVal = utility.ToInt(dict.ConfigValue) + } + + if dict, ok := configDict["member_receive_address"]; !ok || dict.ConfigValue == "" { + logger.Errorf("钱包收款地址不存在") + return statuscode.ServerError + } else if dict.ConfigValue != "" { + toAddress = dict.ConfigValue + } + + if err := e.Orm.Model(&renwalConfig).Where("id =?", req.ConfigId).First(&renwalConfig).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return statuscode.DataError + } + + logger.Errorf("查询套餐详情失败,err:", err) + return statuscode.ServerError + } + + if renwalConfig.Status != memberrenwalconfigstatus.ENABLE { + return statuscode.RenwalConfigDisabled + } + + price := renwalConfig.OriginalPrice + + if renwalConfig.DiscountPrice.Cmp(decimal.Zero) > 0 { + price = renwalConfig.DiscountPrice + } + + amount, err := generateUniqueAmount(price, 3, 5, expirationVal) + + if err != nil { + logger.Errorf("生成唯一金额失败,userId:%v err:%v", req.UserId, err) + return statuscode.ServerError + } + renwalLog.RenwalId = req.ConfigId + renwalLog.UserId = req.UserId + renwalLog.RenwalName = renwalConfig.PackageName + renwalLog.RenwalDuration = renwalConfig.DurationDay + renwalLog.Status = memberrenwallogstatus.PENDING + renwalLog.ExpirationTime = now.Add(time.Minute * time.Duration(expirationVal)) + renwalLog.Coin = "USDT" + renwalLog.PayableAmount = amount + + if err := e.Orm.Model(&models.MemberRenwaLog{}).Create(&renwalLog).Error; err != nil { + logger.Errorf("创建续费记录失败,userId:%v err:%v", req.UserId, err) + return statuscode.ServerError + } + data.Amount = renwalLog.PayableAmount + data.ExpirationTimeUnix = renwalLog.ExpirationTime.UnixNano() / int64(time.Millisecond) + data.NetworkName = "TRC20" + data.ToAddress = toAddress + + return statuscode.OK +} + +// 生成唯一的金额 +// base: 基础金额 +// decimalPlaces: 小数位数 +// maxRetries: 最大尝试次数 +// expirationVal: 过期时间(分钟) +func generateUniqueAmount(base decimal.Decimal, decimalPlaces int, maxRetries int, expirationVal int) (decimal.Decimal, error) { + // 尝试生成最大次数为 maxRetries + for i := 0; i < maxRetries; i++ { + amount := GenerateRandomDecimal(base, decimalPlaces) + + // 如果没有重复,返回生成的金额 + err := helper.DefaultRedis.SetAdd(fmt.Sprintf(rediskey.OrderAmount, amount), "1", time.Duration(expirationVal)) + if err == nil { + // 插入成功,返回该金额 + return amount, nil + } + + // key 已存在,继续尝试 + if err.Error() == "key已存在" { + fmt.Println("金额已存在,重试生成...") + continue + } + // 成功,返回生成的金额 + return amount, nil + } + + // 超过最大重试次数,增加小数位数并递归 + return generateUniqueAmount(base, decimalPlaces+1, maxRetries, expirationVal) +} + +func GenerateRandomDecimal(base decimal.Decimal, decimalPlaces int) decimal.Decimal { + // 生成一个随机的小数部分,最大为 1,但小数位数不超过指定的位数 + rand.Seed(time.Now().UnixNano()) + randomFraction := rand.Float64() + + // 将随机小数调整到指定的小数位数 + randomDecimal := decimal.NewFromFloat(randomFraction).Round(int32(decimalPlaces)) + + // 将原始的 decimal 加上随机小数部分 + result := base.Add(randomDecimal) + + // 返回最终结果,确保精确到指定的小数位数 + return result.Round(int32(decimalPlaces)) +} diff --git a/app/admin/service/appservice/member_withdrawal_log.go b/app/admin/service/appservice/member_withdrawal_log.go new file mode 100644 index 0000000..cc01615 --- /dev/null +++ b/app/admin/service/appservice/member_withdrawal_log.go @@ -0,0 +1,185 @@ +package appservice + +import ( + "errors" + "go-admin/app/admin/models" + "go-admin/app/admin/service/dto" + "go-admin/pkg/utility" + "time" + + memberwithdrawallogstatus "go-admin/common/const/dicts/member_withdrawal_log_status" + cDto "go-admin/common/dto" + statuscode "go-admin/common/status_code" + + "github.com/go-admin-team/go-admin-core/logger" + "github.com/go-admin-team/go-admin-core/sdk/service" + "github.com/jinzhu/copier" + "github.com/shopspring/decimal" + "gorm.io/gorm" +) + +type MemberWithdrawalLog struct { + service.Service +} + +// 分页查询 +func (e *MemberWithdrawalLog) GetPage(req *dto.MemberWithdrawalLogGetPageAppReq, list *[]dto.MemberWithdrawalLogResp, count *int64) int { + var err error + var data models.MemberWithdrawalLog + var datas []models.MemberWithdrawalLog + userIds := make([]int, 0) + + err = e.Orm.Model(&data). + Scopes( + cDto.MakeCondition(req.GetNeedSearch()), + cDto.Paginate(req.GetPageSize(), req.GetPageIndex()), + ). + Find(&datas).Limit(-1).Offset(-1). + Count(count).Error + if err != nil { + e.Log.Errorf("MemberWithdrawalLogService GetPage error:%s \r\n", err) + return statuscode.ServerError + } + + for _, user := range datas { + if !utility.ContainsInt(userIds, user.UserId) { + userIds = append(userIds, user.UserId) + } + } + + userNames := make([]models.LineUser, 0) + e.Orm.Model(&models.LineUser{}).Where("id in (?)", userIds).Select("id,nickname").Find(&userNames) + + for _, v := range datas { + item := dto.MemberWithdrawalLogResp{} + copier.Copy(&item, &v) + + for _, user := range userNames { + if v.UserId == user.Id { + item.NickName = user.Nickname + } + } + + item.CreateTimeUnix = v.CreatedAt.UnixNano() / int64(time.Millisecond) + + if v.ConfirmTime != nil { + item.ConfirmTimeUnix = v.ConfirmTime.UnixNano() / int64(time.Millisecond) + } + + *list = append(*list, item) + } + + return statuscode.OK +} + +// 用户提现申请 +// return statuscode +func (e *MemberWithdrawalLog) Apply(req *dto.MemberWithdrawalLogApplyReq) int { + // config:=adminservice.SysConfig{} + var network models.LineCoinnetwork + + if err := e.Orm.Model(&network).Where("id =?", req.NetworkId).First(&network).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return statuscode.NetworkNotExist + } + + logger.Errorf("查询网络失败 网络id:%v err:%v", req.NetworkId, err) + return statuscode.ServerError + } + + if network.Status != 1 { + return statuscode.NetworkUnAvailable + } + + //小于最小提现金额 + if req.Amount.Cmp(network.MinWithdrawal) < 0 { + return statuscode.BelowMinimum + } + + fee := req.Amount.Mul(network.Fee.Div(decimal.NewFromInt(100))).Truncate(2) + code := statuscode.OK + log := models.MemberWithdrawalLog{ + UserId: req.UserId, + NetworkId: req.NetworkId, + NetworkName: network.NetworkName, + Amount: req.Amount, + ToAddress: req.ToAddress, + Status: memberwithdrawallogstatus.PENDING, + Fee: fee, + } + + //事务 + e.Orm.Transaction(func(tx *gorm.DB) error { + balance := models.MemberBalance{} + + if err := tx.Model(&balance).Where("user_id = ?", req.UserId).First(&balance).Error; err != nil { + code = statuscode.InsufficientFunds + return err + } + + frozenAmount := fee.Add(req.Amount) + if frozenAmount.Cmp(balance.FreeAmount) > 0 { + code = statuscode.InsufficientFunds + return errors.New("余额不足") + } + + balance.FreeAmount = balance.FreeAmount.Sub(frozenAmount) + balance.FrozenAmount = balance.FrozenAmount.Add(frozenAmount) + + if err := tx.Create(&log).Error; err != nil { + code = statuscode.ServerError + logger.Errorf("创建提现记录失败 userid:%d, err:%v", req.UserId, err) + + return err + } + + if err := tx.Model(&balance).Updates(map[string]interface{}{ + "free_amount": balance.FreeAmount, + "frozen_amount": balance.FrozenAmount, + }).Error; err != nil { + code = statuscode.ServerError + logger.Errorf("更新余额失败 userid:%v err:%v", req.UserId, err) + return err + } + + return nil + }) + + return code +} + +// 取消提现 +func (e *MemberWithdrawalLog) Canceled(req *dto.MemberWithdrawalLogCancelReq, userId int) int { + code := statuscode.OK + + e.Orm.Transaction(func(tx *gorm.DB) error { + var log models.MemberWithdrawalLog + if err := tx.Model(&log).Where("id = ? AND user_id =?", req.Id, userId).First(&log).Error; err != nil { + code = statuscode.DataError + return err + } + + if log.Status != memberwithdrawallogstatus.PENDING { + code = statuscode.CanNotCancel + return errors.New("提现记录状态错误") + } + + log.Status = memberwithdrawallogstatus.CANCELED + totalAmount := log.Amount.Add(log.Fee) + + if err := tx.Exec("UPDATE member_balance SET free_amount=free_amount+?,frozen_amount=frozen_amount-? WHERE user_id =?", totalAmount, totalAmount, log.UserId).Error; err != nil { + code = statuscode.ServerError + logger.Errorf("更新余额失败 提现订单:id userid:%v err:%v", req.Id, userId, err) + return err + } + + if err := tx.Model(&log).Update("status", log.Status).Error; err != nil { + code = statuscode.ServerError + logger.Errorf("更新提现订单状态失败 提现订单:id userid:%v err:%v", req.Id, userId, err) + return err + } + return nil + }) + + return code +} diff --git a/app/admin/service/dto/line_coinnetwork.go b/app/admin/service/dto/line_coinnetwork.go index 86dfda0..e063852 100644 --- a/app/admin/service/dto/line_coinnetwork.go +++ b/app/admin/service/dto/line_coinnetwork.go @@ -48,6 +48,7 @@ type LineCoinnetworkInsertReq struct { UnlockTime int64 `json:"unlockTime" comment:"提现确认平均时间,单位分钟"` Fee decimal.Decimal `json:"fee" comment:"网络手续费百分比"` MinWithdrawal decimal.Decimal `json:"minWithdrawal" comment:"最小提现金额"` + Status int `json:"status" comment:"状态"` common.ControlBy } @@ -62,6 +63,7 @@ func (s *LineCoinnetworkInsertReq) Generate(model *models.LineCoinnetwork) { model.UnlockTime = s.UnlockTime model.Fee = s.Fee model.MinWithdrawal = s.MinWithdrawal + model.Status = s.Status model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 } @@ -78,6 +80,7 @@ type LineCoinnetworkUpdateReq struct { UnlockTime int64 `json:"unlockTime" comment:"提现确认平均时间,单位分钟"` Fee decimal.Decimal `json:"fee" comment:"网络手续费百分比"` MinWithdrawal decimal.Decimal `json:"minWithdrawal" comment:"最小提现金额"` + Status int `json:"status" comment:"状态"` common.ControlBy } @@ -92,6 +95,7 @@ func (s *LineCoinnetworkUpdateReq) Generate(model *models.LineCoinnetwork) { model.UnlockTime = s.UnlockTime model.Fee = s.Fee model.MinWithdrawal = s.MinWithdrawal + model.Status = s.Status model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 } diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index 0d43269..a4e3801 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -208,6 +208,63 @@ type LineAddPreOrderReq struct { Ext []LineAddPreOrderExtReq `json:"ext" ` //拓展字段 } +type LinePreOrderAddPositionReq struct { + ExchangeType string `json:"exchangeType" vd:"len($)>0"` //交易所类型 + OrderType int `json:"orderType"` //订单类型 + Symbol string `json:"symbol"` //交易对 + ApiUserId int `json:"apiId" ` //下单用户 + Site string `json:"site" ` //购买方向 + BuyPrice decimal.Decimal `json:"buyPrice" vd:"$>0"` //购买金额 U + PricePattern string `json:"pricePattern"` //价格模式 + Price decimal.Decimal `json:"price" vd:"$>0"` //下单价百分比 + Profit decimal.Decimal `json:"profit" vd:"$>0"` //止盈价 + // StopPrice string `json:"stop_price"` //止损价 + AddPositionOrderType string `json:"addPositionOrderType" comment:"加仓订单类型 LIMIT-限价 MARKET-市价"` + PriceType string `json:"priceType"` //价格类型 + ReTakeProfitRatio decimal.Decimal `json:"reTakeProfitRatio" comment:"亏损回本止盈百分比"` + ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" comment:"减仓价格百分比" ` + ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" comment:"减仓数量百分比" ` + ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" comment:"减仓后止盈百分比" ` + ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" comment:"减仓后止损百分比"` +} + +// 校验 +func (req *LinePreOrderAddPositionReq) Valid() error { + if req.ExchangeType == "" { + return errors.New("交易所类型不能为空") + } + + if req.OrderType <= 0 || req.OrderType > 2 { + return errors.New("订单类型错误") + } + + if req.Symbol == "" { + return errors.New("交易对不能为空") + } + + if req.AddPositionOrderType == "" { + return errors.New("加仓订单类型不能为空") + } + + if req.PriceType == "" { + return errors.New("价格类型不能为空") + } + + if req.ReducePriceRatio.IsZero() { + return errors.New("主单减仓价格百分比不能为空") + } + + if req.ReduceNumRatio.IsZero() { + return errors.New("主单减仓数量百分比不能为空") + } + + if req.ReduceTakeProfitRatio.IsZero() { + return errors.New("减仓后止盈百分比不能为空") + } + + return nil +} + func (req LineAddPreOrderReq) Valid() error { if req.ReducePriceRatio.IsZero() { return errors.New("主单减仓价格百分比不能为空") diff --git a/app/admin/service/dto/line_user.go b/app/admin/service/dto/line_user.go index 24f0c9b..6422b22 100644 --- a/app/admin/service/dto/line_user.go +++ b/app/admin/service/dto/line_user.go @@ -1,12 +1,13 @@ package dto import ( - "github.com/shopspring/decimal" statuscode "go-admin/common/status_code" "go-admin/pkg/emailhelper" "go-admin/pkg/utility" "time" + "github.com/shopspring/decimal" + "go-admin/app/admin/models" "go-admin/common/dto" common "go-admin/common/models" @@ -330,3 +331,16 @@ type VtsRechargePreOrderResp struct { PriceAmount string `json:"priceAmount"` PriceCurrency string `json:"priceCurrency"` } + +type PersonnalInviteLogPageReq struct { + dto.Pagination `search:"-"` + UserId int `json:"userId" form:"userId"` +} + +type InviteLogResp struct { + Id int `json:"id"` + UserId int `json:"userId"` + RegisterTime int64 `json:"registerTime"` + NickName string `json:"nickName"` + Childs []InviteLogResp `json:"childs"` +} diff --git a/app/admin/service/dto/member_balance_log.go b/app/admin/service/dto/member_balance_log.go index d2e5923..7e1ce3c 100644 --- a/app/admin/service/dto/member_balance_log.go +++ b/app/admin/service/dto/member_balance_log.go @@ -1,34 +1,34 @@ package dto import ( - "go-admin/app/admin/models" "go-admin/common/dto" common "go-admin/common/models" + + "github.com/shopspring/decimal" ) type MemberBalanceLogGetPageReq struct { - dto.Pagination `search:"-"` - ChangeSource string `form:"changeSource" search:"type:exact;column:change_source;table:member_balance_log" comment:"变更来源 (member_change_source)"` - ChangeType string `form:"changeType" search:"type:exact;column:change_type;table:member_balance_log" comment:"变更类别 1-收入 2-支出"` - MemberBalanceLogOrder + dto.Pagination `search:"-"` + ChangeSource string `form:"changeSource" search:"type:exact;column:change_source;table:member_balance_log" comment:"变更来源 (member_change_source)"` + ChangeType string `form:"changeType" search:"type:exact;column:change_type;table:member_balance_log" comment:"变更类别 1-收入 2-支出"` + MemberBalanceLogOrder } type MemberBalanceLogOrder struct { - Id string `form:"idOrder" search:"type:order;column:id;table:member_balance_log"` - UserId string `form:"userIdOrder" search:"type:order;column:user_id;table:member_balance_log"` - ChangeSource string `form:"changeSourceOrder" search:"type:order;column:change_source;table:member_balance_log"` - ChangeType string `form:"changeTypeOrder" search:"type:order;column:change_type;table:member_balance_log"` - Amount string `form:"amountOrder" search:"type:order;column:amount;table:member_balance_log"` - BalanceBefore string `form:"balanceBeforeOrder" search:"type:order;column:balance_before;table:member_balance_log"` - BalanceAfter string `form:"balanceAfterOrder" search:"type:order;column:balance_after;table:member_balance_log"` - Remark string `form:"remarkOrder" search:"type:order;column:remark;table:member_balance_log"` - CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:member_balance_log"` - UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:member_balance_log"` - DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:member_balance_log"` - CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:member_balance_log"` - UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:member_balance_log"` - + Id string `form:"idOrder" search:"type:order;column:id;table:member_balance_log"` + UserId string `form:"userIdOrder" search:"type:order;column:user_id;table:member_balance_log"` + ChangeSource string `form:"changeSourceOrder" search:"type:order;column:change_source;table:member_balance_log"` + ChangeType string `form:"changeTypeOrder" search:"type:order;column:change_type;table:member_balance_log"` + Amount string `form:"amountOrder" search:"type:order;column:amount;table:member_balance_log"` + BalanceBefore string `form:"balanceBeforeOrder" search:"type:order;column:balance_before;table:member_balance_log"` + BalanceAfter string `form:"balanceAfterOrder" search:"type:order;column:balance_after;table:member_balance_log"` + Remark string `form:"remarkOrder" search:"type:order;column:remark;table:member_balance_log"` + CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:member_balance_log"` + UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:member_balance_log"` + DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:member_balance_log"` + CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:member_balance_log"` + UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:member_balance_log"` } func (m *MemberBalanceLogGetPageReq) GetNeedSearch() interface{} { @@ -36,29 +36,29 @@ func (m *MemberBalanceLogGetPageReq) GetNeedSearch() interface{} { } type MemberBalanceLogInsertReq struct { - Id int `json:"-" comment:"主键"` // 主键 - UserId int `json:"userId" comment:"用户id"` - ChangeSource string `json:"changeSource" comment:"变更来源 (member_change_source)"` - ChangeType string `json:"changeType" comment:"变更类别 1-收入 2-支出"` - Amount string `json:"amount" comment:"变更金额"` - BalanceBefore string `json:"balanceBefore" comment:"变更前余额"` - BalanceAfter string `json:"balanceAfter" comment:"变更后余额"` - Remark string `json:"remark" comment:"备注"` - common.ControlBy + Id int `json:"-" comment:"主键"` // 主键 + UserId int `json:"userId" comment:"用户id"` + ChangeSource string `json:"changeSource" comment:"变更来源 (member_change_source)"` + ChangeType int `json:"changeType" comment:"变更类别 1-收入 2-支出"` + Amount decimal.Decimal `json:"amount" comment:"变更金额"` + BalanceBefore decimal.Decimal `json:"balanceBefore" comment:"变更前余额"` + BalanceAfter decimal.Decimal `json:"balanceAfter" comment:"变更后余额"` + Remark string `json:"remark" comment:"备注"` + common.ControlBy } -func (s *MemberBalanceLogInsertReq) Generate(model *models.MemberBalanceLog) { - if s.Id == 0 { - model.Model = common.Model{ Id: s.Id } - } - model.UserId = s.UserId - model.ChangeSource = s.ChangeSource - model.ChangeType = s.ChangeType - model.Amount = s.Amount - model.BalanceBefore = s.BalanceBefore - model.BalanceAfter = s.BalanceAfter - model.Remark = s.Remark - model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 +func (s *MemberBalanceLogInsertReq) Generate(model *models.MemberBalanceLog) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.UserId = s.UserId + model.ChangeSource = s.ChangeSource + model.ChangeType = s.ChangeType + model.Amount = s.Amount + model.BalanceBefore = s.BalanceBefore + model.BalanceAfter = s.BalanceAfter + model.Remark = s.Remark + model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 } func (s *MemberBalanceLogInsertReq) GetId() interface{} { @@ -66,29 +66,29 @@ func (s *MemberBalanceLogInsertReq) GetId() interface{} { } type MemberBalanceLogUpdateReq struct { - Id int `uri:"id" comment:"主键"` // 主键 - UserId int `json:"userId" comment:"用户id"` - ChangeSource string `json:"changeSource" comment:"变更来源 (member_change_source)"` - ChangeType string `json:"changeType" comment:"变更类别 1-收入 2-支出"` - Amount string `json:"amount" comment:"变更金额"` - BalanceBefore string `json:"balanceBefore" comment:"变更前余额"` - BalanceAfter string `json:"balanceAfter" comment:"变更后余额"` - Remark string `json:"remark" comment:"备注"` - common.ControlBy + Id int `uri:"id" comment:"主键"` // 主键 + UserId int `json:"userId" comment:"用户id"` + ChangeSource string `json:"changeSource" comment:"变更来源 (member_change_source)"` + ChangeType int `json:"changeType" comment:"变更类别 1-收入 2-支出"` + Amount decimal.Decimal `json:"amount" comment:"变更金额"` + BalanceBefore decimal.Decimal `json:"balanceBefore" comment:"变更前余额"` + BalanceAfter decimal.Decimal `json:"balanceAfter" comment:"变更后余额"` + Remark string `json:"remark" comment:"备注"` + common.ControlBy } -func (s *MemberBalanceLogUpdateReq) Generate(model *models.MemberBalanceLog) { - if s.Id == 0 { - model.Model = common.Model{ Id: s.Id } - } - model.UserId = s.UserId - model.ChangeSource = s.ChangeSource - model.ChangeType = s.ChangeType - model.Amount = s.Amount - model.BalanceBefore = s.BalanceBefore - model.BalanceAfter = s.BalanceAfter - model.Remark = s.Remark - model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 +func (s *MemberBalanceLogUpdateReq) Generate(model *models.MemberBalanceLog) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.UserId = s.UserId + model.ChangeSource = s.ChangeSource + model.ChangeType = s.ChangeType + model.Amount = s.Amount + model.BalanceBefore = s.BalanceBefore + model.BalanceAfter = s.BalanceAfter + model.Remark = s.Remark + model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 } func (s *MemberBalanceLogUpdateReq) GetId() interface{} { @@ -97,8 +97,9 @@ func (s *MemberBalanceLogUpdateReq) GetId() interface{} { // MemberBalanceLogGetReq 功能获取请求参数 type MemberBalanceLogGetReq struct { - Id int `uri:"id"` + Id int `uri:"id"` } + func (s *MemberBalanceLogGetReq) GetId() interface{} { return s.Id } @@ -111,3 +112,22 @@ type MemberBalanceLogDeleteReq struct { func (s *MemberBalanceLogDeleteReq) GetId() interface{} { return s.Ids } + +type MemberBalanceLogPageAppReq struct { + dto.Pagination `search:"-"` + ChangeSource string `form:"changeSource" search:"type:exact;column:change_source;table:member_balance_log" comment:"变更来源 (member_change_source)"` + ChangeType string `form:"changeType" search:"type:exact;column:change_type;table:member_balance_log" comment:"变更类别 1-收入 2-支出"` + UserId int `json:"userId"` + MemberBalanceLogOrder +} + +type MemberBalanceLogAppResp struct { + Id int `json:"-" comment:"主键"` // 主键 + UserId int `json:"userId" comment:"用户id"` + UserName string `json:"userName" comment:"用户名"` + ChangeSource string `json:"changeSource" comment:"变更来源 (member_change_source)"` + ChangeType int `json:"changeType" comment:"变更类别 1-收入 2-支出"` + Amount decimal.Decimal `json:"amount" comment:"变更金额"` + Remark string `json:"remark" comment:"备注"` + CreateTimeUnix int64 `json:"createTime" comment:"创建时间"` +} diff --git a/app/admin/service/dto/member_renwa_log.go b/app/admin/service/dto/member_renwa_log.go index 2c8fa22..4d85684 100644 --- a/app/admin/service/dto/member_renwa_log.go +++ b/app/admin/service/dto/member_renwa_log.go @@ -6,6 +6,8 @@ import ( "go-admin/app/admin/models" "go-admin/common/dto" common "go-admin/common/models" + + "github.com/shopspring/decimal" ) type MemberRenwaLogGetPageReq struct { @@ -18,15 +20,16 @@ type MemberRenwalLogPageAppReq struct { } type MemberRenwalLogResp struct { - Id int `json:"id"` - RenwalName string `json:"renwalName" comment:"续期套餐名称"` - RenwalDuration string `json:"renwalDuration" comment:"续期时长(天数)"` - Status string `json:"status" comment:"订单状态(member_renwal_log_status)"` - PayableAmount string `json:"payableAmount" comment:"应付金额"` - ActualPaymentAmount string `json:"actualPaymentAmount" comment:"实付金额"` - FromAddress string `json:"fromAddress" comment:"付款地址"` - Coin string `json:"coin" comment:"代币"` - PaymentTime time.Time `json:"paymentTime" comment:"支付时间"` + Id int `json:"id"` + RenwalName string `json:"renwalName" comment:"续期套餐名称"` + RenwalDuration int `json:"renwalDuration" comment:"续期时长(天数)"` + Status string `json:"status" comment:"订单状态(member_renwal_log_status)"` + PayableAmount decimal.Decimal `json:"payableAmount" comment:"应付金额"` + ActualPaymentAmount decimal.Decimal `json:"actualPaymentAmount" comment:"实付金额"` + FromAddress string `json:"fromAddress" comment:"付款地址"` + Coin string `json:"coin" comment:"代币"` + PaymentTimeUnix int64 `json:"paymentTimeUnix" comment:"支付时间"` + ExpirationTimeUnix int64 `json:"expirationTimeUnix" comment:"过期时间"` } type MemberRenwaLogOrder struct { @@ -52,16 +55,16 @@ func (m *MemberRenwaLogGetPageReq) GetNeedSearch() interface{} { } type MemberRenwaLogInsertReq struct { - Id int `json:"-" comment:"主键"` // 主键 - RenwalId string `json:"renwalId" comment:"套餐id"` - RenwalName string `json:"renwalName" comment:"续期套餐名称"` - RenwalDuration string `json:"renwalDuration" comment:"续期时长(天数)"` - Status string `json:"status" comment:"订单状态(member_renwal_log_status)"` - PayableAmount string `json:"payableAmount" comment:"应付金额"` - ActualPaymentAmount string `json:"actualPaymentAmount" comment:"实付金额"` - FromAddress string `json:"fromAddress" comment:"付款地址"` - Coin string `json:"coin" comment:"代币"` - PaymentTime time.Time `json:"paymentTime" comment:"支付时间"` + Id int `json:"-" comment:"主键"` // 主键 + RenwalId int `json:"renwalId" comment:"套餐id"` + RenwalName string `json:"renwalName" comment:"续期套餐名称"` + RenwalDuration int `json:"renwalDuration" comment:"续期时长(天数)"` + Status string `json:"status" comment:"订单状态(member_renwal_log_status)"` + PayableAmount decimal.Decimal `json:"payableAmount" comment:"应付金额"` + ActualPaymentAmount decimal.Decimal `json:"actualPaymentAmount" comment:"实付金额"` + FromAddress string `json:"fromAddress" comment:"付款地址"` + Coin string `json:"coin" comment:"代币"` + PaymentTime time.Time `json:"paymentTime" comment:"支付时间"` common.ControlBy } @@ -77,7 +80,7 @@ func (s *MemberRenwaLogInsertReq) Generate(model *models.MemberRenwaLog) { model.ActualPaymentAmount = s.ActualPaymentAmount model.FromAddress = s.FromAddress model.Coin = s.Coin - model.PaymentTime = s.PaymentTime + // model.PaymentTime = s.PaymentTime model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 } @@ -86,16 +89,16 @@ func (s *MemberRenwaLogInsertReq) GetId() interface{} { } type MemberRenwaLogUpdateReq struct { - Id int `uri:"id" comment:"主键"` // 主键 - RenwalId string `json:"renwalId" comment:"套餐id"` - RenwalName string `json:"renwalName" comment:"续期套餐名称"` - RenwalDuration string `json:"renwalDuration" comment:"续期时长(天数)"` - Status string `json:"status" comment:"订单状态(member_renwal_log_status)"` - PayableAmount string `json:"payableAmount" comment:"应付金额"` - ActualPaymentAmount string `json:"actualPaymentAmount" comment:"实付金额"` - FromAddress string `json:"fromAddress" comment:"付款地址"` - Coin string `json:"coin" comment:"代币"` - PaymentTime time.Time `json:"paymentTime" comment:"支付时间"` + Id int `uri:"id" comment:"主键"` // 主键 + RenwalId int `json:"renwalId" comment:"套餐id"` + RenwalName string `json:"renwalName" comment:"续期套餐名称"` + RenwalDuration int `json:"renwalDuration" comment:"续期时长(天数)"` + Status string `json:"status" comment:"订单状态(member_renwal_log_status)"` + PayableAmount decimal.Decimal `json:"payableAmount" comment:"应付金额"` + ActualPaymentAmount decimal.Decimal `json:"actualPaymentAmount" comment:"实付金额"` + FromAddress string `json:"fromAddress" comment:"付款地址"` + Coin string `json:"coin" comment:"代币"` + // PaymentTime time.Time `json:"paymentTime" comment:"支付时间"` common.ControlBy } @@ -111,7 +114,7 @@ func (s *MemberRenwaLogUpdateReq) Generate(model *models.MemberRenwaLog) { model.ActualPaymentAmount = s.ActualPaymentAmount model.FromAddress = s.FromAddress model.Coin = s.Coin - model.PaymentTime = s.PaymentTime + // model.PaymentTime = s.PaymentTime model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 } @@ -136,3 +139,15 @@ type MemberRenwaLogDeleteReq struct { func (s *MemberRenwaLogDeleteReq) GetId() interface{} { return s.Ids } + +type MemberRenwalCreateAppReq struct { + ConfigId int `json:"configId"` + UserId int `json:"-"` +} + +type MemberRenwalCreateAppResp struct { + NetworkName string `json:"networkName"` //网络名称 + Amount decimal.Decimal `json:"amount"` + ToAddress string `json:"toAddress"` //收款地址 + ExpirationTimeUnix int64 `json:"expirationTimeUnix"` //过期时间 +} diff --git a/app/admin/service/dto/member_withdrawal_log.go b/app/admin/service/dto/member_withdrawal_log.go new file mode 100644 index 0000000..ad4bac4 --- /dev/null +++ b/app/admin/service/dto/member_withdrawal_log.go @@ -0,0 +1,183 @@ +package dto + +import ( + "time" + + "go-admin/app/admin/models" + "go-admin/common/dto" + common "go-admin/common/models" + statuscode "go-admin/common/status_code" + + "github.com/shopspring/decimal" +) + +type MemberWithdrawalLogGetPageReq struct { + dto.Pagination `search:"-"` + NetworkName string `form:"networkName" search:"type:contains;column:network_name;table:member_withdrawal_log" comment:"网络名称"` + Status string `form:"status" search:"type:exact;column:status;table:member_withdrawal_log" comment:"提现状态(member_withdrawal_status)"` + MemberWithdrawalLogOrder +} + +type MemberWithdrawalLogGetPageAppReq struct { + dto.Pagination `search:"-"` + NetworkName string `form:"networkName" search:"type:contains;column:network_name;table:member_withdrawal_log" comment:"网络名称"` + Status string `form:"status" search:"type:exact;column:status;table:member_withdrawal_log" comment:"提现状态(member_withdrawal_status)"` + UserId int `form:"userId" search:"type:exact;column:user_id;table:member_withdrawal_log" comment:"用户id"` + + MemberWithdrawalLogOrder +} + +func (m *MemberWithdrawalLogGetPageAppReq) GetNeedSearch() interface{} { + return *m +} + +type MemberWithdrawalLogOrder struct { + Id string `form:"idOrder" search:"type:order;column:id;table:member_withdrawal_log"` + NetworkId string `form:"networkIdOrder" search:"type:order;column:network_id;table:member_withdrawal_log"` + NetworkName string `form:"networkNameOrder" search:"type:order;column:network_name;table:member_withdrawal_log"` + UserId string `form:"userIdOrder" search:"type:order;column:user_id;table:member_withdrawal_log"` + Amount string `form:"amountOrder" search:"type:order;column:amount;table:member_withdrawal_log"` + Status string `form:"statusOrder" search:"type:order;column:status;table:member_withdrawal_log"` + ConfirmTime string `form:"confirmTimeOrder" search:"type:order;column:confirm_time;table:member_withdrawal_log"` + Fee string `form:"feeOrder" search:"type:order;column:fee;table:member_withdrawal_log"` + Remark string `form:"remarkOrder" search:"type:order;column:remark;table:member_withdrawal_log"` + CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:member_withdrawal_log"` + UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:member_withdrawal_log"` + DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:member_withdrawal_log"` + CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:member_withdrawal_log"` + UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:member_withdrawal_log"` +} + +func (m *MemberWithdrawalLogGetPageReq) GetNeedSearch() interface{} { + return *m +} + +type MemberWithdrawalLogInsertReq struct { + Id int `json:"-" comment:"主键"` // 主键 + NetworkId int `json:"networkId" comment:"网络id"` + NetworkName string `json:"networkName" comment:"网络名称"` + UserId int `json:"userId" comment:"用户id"` + Amount decimal.Decimal `json:"amount" comment:"提现金额(U)"` + Status string `json:"status" comment:"提现状态(member_withdrawal_status)"` + ConfirmTime *time.Time `json:"confirmTime" comment:"确认时间"` + Fee decimal.Decimal `json:"fee" comment:"手续费比例"` + Remark string `json:"remark" comment:"备注"` + common.ControlBy +} + +func (s *MemberWithdrawalLogInsertReq) Generate(model *models.MemberWithdrawalLog) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.NetworkId = s.NetworkId + model.NetworkName = s.NetworkName + model.UserId = s.UserId + model.Amount = s.Amount + model.Status = s.Status + model.ConfirmTime = s.ConfirmTime + model.Fee = s.Fee + model.Remark = s.Remark + model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 +} + +func (s *MemberWithdrawalLogInsertReq) GetId() interface{} { + return s.Id +} + +type MemberWithdrawalLogUpdateReq struct { + Id int `uri:"id" comment:"主键"` // 主键 + NetworkId int `json:"networkId" comment:"网络id"` + NetworkName string `json:"networkName" comment:"网络名称"` + UserId int `json:"userId" comment:"用户id"` + Amount decimal.Decimal `json:"amount" comment:"提现金额(U)"` + Status string `json:"status" comment:"提现状态(member_withdrawal_status)"` + ConfirmTime *time.Time `json:"confirmTime" comment:"确认时间"` + Fee decimal.Decimal `json:"fee" comment:"手续费比例"` + Remark string `json:"remark" comment:"备注"` + common.ControlBy +} + +func (s *MemberWithdrawalLogUpdateReq) Generate(model *models.MemberWithdrawalLog) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.NetworkId = s.NetworkId + model.NetworkName = s.NetworkName + model.UserId = s.UserId + model.Amount = s.Amount + model.Status = s.Status + model.ConfirmTime = s.ConfirmTime + model.Fee = s.Fee + model.Remark = s.Remark + model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 +} + +func (s *MemberWithdrawalLogUpdateReq) GetId() interface{} { + return s.Id +} + +// MemberWithdrawalLogGetReq 功能获取请求参数 +type MemberWithdrawalLogGetReq struct { + Id int `uri:"id"` +} + +func (s *MemberWithdrawalLogGetReq) GetId() interface{} { + return s.Id +} + +// MemberWithdrawalLogDeleteReq 功能删除请求参数 +type MemberWithdrawalLogDeleteReq struct { + Ids []int `json:"ids"` +} + +func (s *MemberWithdrawalLogDeleteReq) GetId() interface{} { + return s.Ids +} + +type MemberWithdrawalLogCancelReq struct { + Id int `json:"id"` +} + +func (s *MemberWithdrawalLogCancelReq) Valid() int { + if s.Id <= 0 { + return statuscode.ParamErr + } + + return statuscode.OK +} + +type MemberWithdrawalLogApplyReq struct { + NetworkId int `json:"networkId" comment:"网络ID"` + UserId int `json:"userId" comment:"用户ID"` + Amount decimal.Decimal `json:"amount" comment:"提现金额"` + ToAddress string `json:"toAddress" comment:"提现地址"` +} + +func (e *MemberWithdrawalLogApplyReq) Valid() int { + if e.NetworkId <= 0 { + return statuscode.ParamErr + } + + if e.Amount.Cmp(decimal.Zero) <= 0 { + return statuscode.ParamErr + } + + if e.ToAddress == "" { + return statuscode.ParamErr + } + return statuscode.OK +} + +type MemberWithdrawalLogResp struct { + Id int `json:"id"` + NetworkId int `json:"networkId"` + UserId int `json:"userId"` + NickName string `json:"nickName"` + Amount decimal.Decimal `json:"amount"` + Fee decimal.Decimal `json:"fee"` + ToAddress string `json:"toAddress"` + Hash string `json:"hash"` + Status string `json:"status"` + CreateTimeUnix int64 `json:"createTime"` + ConfirmTimeUnix int64 `json:"confirmTime"` +} diff --git a/app/admin/service/line_coinnetwork.go b/app/admin/service/line_coinnetwork.go index 852fe90..00de61e 100644 --- a/app/admin/service/line_coinnetwork.go +++ b/app/admin/service/line_coinnetwork.go @@ -64,6 +64,13 @@ func (e *LineCoinnetwork) Insert(c *dto.LineCoinnetworkInsertReq) error { var err error var data models.LineCoinnetwork c.Generate(&data) + var count int64 + e.Orm.Model(&data).Where("network_name =?").Count(&count) + + if count > 0 { + return errors.New("网络已存在") + } + err = e.Orm.Create(&data).Error if err != nil { e.Log.Errorf("LineCoinnetworkService Insert error:%s \r\n", err) @@ -76,6 +83,13 @@ func (e *LineCoinnetwork) Insert(c *dto.LineCoinnetworkInsertReq) error { func (e *LineCoinnetwork) Update(c *dto.LineCoinnetworkUpdateReq, p *actions.DataPermission) error { var err error var data = models.LineCoinnetwork{} + var count int64 + e.Orm.Model(&data).Where("network_name =? AND id !=?", c.NetworkName, c.Id).Count(&count) + + if count > 0 { + return errors.New("网络已存在") + } + e.Orm.Scopes( actions.Permission(data.TableName(), p), ).First(&data, c.GetId()) diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 2d7a214..7f689e4 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -509,14 +509,6 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //加仓、减仓状态 tx.Model(&models.LinePreOrderStatus{}).Create(&preOrderStatus) - // for index := range preOrderExts { - // if index == 0 { - // preOrderExts[index].OrderId = AddOrder.Id - // } - - // preOrderExts[index].MainOrderId = AddOrder.Id - // } - list := dto.PreOrderRedisList{ Id: AddOrder.Id, Symbol: AddOrder.Symbol, @@ -1749,5 +1741,165 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio } return nil - +} + +// 手动加仓 +func (e *LinePreOrder) AddPosition(req *dto.LinePreOrderAddPositionReq) error { + lastPositionOrder := models.LinePreOrder{} + var tradeSet models2.TradeSet + + if req.OrderType == 1 { + tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 0) + } else if req.OrderType == 2 { + tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 1) + } else { + return fmt.Errorf("交易对:%s 订单类型错误", req.Symbol) + } + + if tradeSet.LastPrice == "" { + return fmt.Errorf("交易对:%s 交易对配置错误", req.Symbol) + } + + if err := e.Orm.Model(&lastPositionOrder).Where("symbol =? AND status = 6 AND site =? AND api_id =? AND symbol_type =? AND exchange_type=?", + req.Symbol, req.Site, req.ApiUserId, req.OrderType, req.ExchangeType).Error; err != nil { + logger.Errorf("交易对:%s查询已开仓订单失败", req.Symbol) + return fmt.Errorf("交易对:%s 没有已开仓的订单", req.Symbol) + } + + ext := models.LinePreOrderExt{ + MainOrderId: lastPositionOrder.Id, + TakeProfitRatio: req.Profit, + ReducePriceRatio: req.ReducePriceRatio, + ReduceNumRatio: req.ReduceNumRatio, + ReduceTakeProfitRatio: req.ReduceTakeProfitRatio, + ReduceStopLossRatio: req.ReduceStopLossRatio, + AddPositionOrderType: req.AddPositionOrderType, + AddPositionType: 2, + AddPositionVal: req.BuyPrice, + } + + addPosition := models.LinePreOrder{ + SignPrice: tradeSet.LastPrice, + Pid: lastPositionOrder.Id, + MainId: lastPositionOrder.Id, + Symbol: req.Symbol, + QuoteSymbol: tradeSet.Currency, + SignPriceU: utility.StrToDecimal(tradeSet.LastPrice), + ApiId: req.ApiUserId, + Site: req.Site, + ExchangeType: req.ExchangeType, + OrderType: 0, + OrderCategory: 3, + BuyPrice: req.BuyPrice.String(), + Status: 0, + SymbolType: req.OrderType, + MainOrderType: req.AddPositionOrderType, + ExpireTime: time.Now().Add(4), + OrderSn: utility.Int64ToString(snowflakehelper.GetOrderId()), + } + + tickerPrice := utility.StrToDecimal(tradeSet.LastPrice) + if req.PricePattern == "percentage" { + addPosition.Rate = req.Price.String() + priceRate := req.Price.Div(decimal.NewFromInt(100)) //下单价除100 =0.1 + + if strings.ToUpper(req.Site) == "BUY" { //购买方向 + //实际下单价格 + truncate := tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit)) + addPosition.Price = truncate.String() + } else { + truncate := tickerPrice.Mul(decimal.NewFromInt(1).Add(priceRate)).Truncate(int32(tradeSet.PriceDigit)) + addPosition.Price = truncate.String() + } + + } else { //实际价格下单 + addPosition.Price = req.Price.Truncate(int32(tradeSet.PriceDigit)).String() + addPosition.SignPriceType = req.PricePattern + addPosition.Rate = "0" + } + if tradeSet.Currency != "USDT" { //不是U本位 + //获取币本位兑换u的价格 + ticker2 := models2.TradeSet{} + tickerVal, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, strings.ToUpper(tradeSet.Coin+"USDT"))) + + if tickerVal == "" { + logger.Error("查询行情失败") + return fmt.Errorf("交易对:%s 获取u本位行情失败", req.Symbol) + } + + err := sonic.Unmarshal([]byte(tickerVal), &ticker2) + + if ticker2.LastPrice == "" { + logger.Errorf("查询行情失败 %s err:%v", strings.ToUpper(tradeSet.Coin+"USDT"), err) + return fmt.Errorf("交易对:%s 获取u本位行情 反序列化失败", req.Symbol) + } + //LTCBTC --> LTCUSDT + uTickerPrice := utility.StrToDecimal(ticker2.LastPrice) //94069 + //换算成U + //div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365 + //在换算成对应交易对对应的价值 + //LTCBTC --> LTCUSDT == LTCUSDT -- 100.502 + div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice)) + //计算下单数量 + addPosition.Num = req.BuyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String() + } else { + fromString, _ := decimal.NewFromString(addPosition.Price) + addPosition.Num = req.BuyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String() + } + //事务保存 + err := e.Orm.Transaction(func(tx *gorm.DB) error { + //添加加仓单 + if err := tx.Create(&addPosition).Error; err != nil { + return err + } + + //止盈、减仓 + orders, err := makeFuturesTakeAndReduce(&addPosition, ext, tradeSet) + + if err != nil { + logger.Error("构造加仓单止盈、减仓失败") + return err + } + + if err := e.Orm.Create(&orders).Error; err != nil { + logger.Error("保存加仓单止盈、减仓失败") + return err + } + + //处理减仓单 + for index := range orders { + //减仓单且 减仓比例大于0 小于100 就冲下止盈止损 + if orders[index].OrderType == 4 && ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 && ext.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { + reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), ext, tradeSet) + + if err != nil { + logger.Error("生产加仓单止盈、减仓失败") + return err + } + + if len(reduceChildOrders) == 0 { + continue + } + + if err := e.Orm.Create(&reduceChildOrders).Error; err != nil { + logger.Error("报错减仓后止盈止损失败") + return err + } + } + } + + ext.OrderId = addPosition.Id + if err := tx.Create(&ext).Error; err != nil { + return err + } + + return nil + }) + + if err != nil { + logger.Errorf("交易对:%s 添加加仓订单失败", req.Symbol) + return fmt.Errorf("交易对:%s 添加加仓订单失败", req.Symbol) + } + + return nil } diff --git a/app/admin/service/member_balance.go b/app/admin/service/member_balance.go index 61e1bb0..1f59e3e 100644 --- a/app/admin/service/member_balance.go +++ b/app/admin/service/member_balance.go @@ -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 *MemberBalance) Get(d *dto.MemberBalanceGetReq, p *actions.DataPermissio // Insert 创建MemberBalance对象 func (e *MemberBalance) Insert(c *dto.MemberBalanceInsertReq) error { - var err error - var data models.MemberBalance - c.Generate(&data) + var err error + var data models.MemberBalance + c.Generate(&data) err = e.Orm.Create(&data).Error if err != nil { e.Log.Errorf("MemberBalanceService Insert error:%s \r\n", err) @@ -72,22 +72,22 @@ func (e *MemberBalance) Insert(c *dto.MemberBalanceInsertReq) error { // Update 修改MemberBalance对象 func (e *MemberBalance) Update(c *dto.MemberBalanceUpdateReq, p *actions.DataPermission) error { - var err error - var data = models.MemberBalance{} - e.Orm.Scopes( - actions.Permission(data.TableName(), p), - ).First(&data, c.GetId()) - c.Generate(&data) + var err error + var data = models.MemberBalance{} + 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("MemberBalanceService 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("MemberBalanceService Save error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权更新该数据") + } + return nil } // Remove 删除MemberBalance @@ -99,11 +99,23 @@ func (e *MemberBalance) Remove(d *dto.MemberBalanceDeleteReq, p *actions.DataPer actions.Permission(data.TableName(), p), ).Delete(&data, d.GetId()) if err := db.Error; err != nil { - e.Log.Errorf("Service RemoveMemberBalance error:%s \r\n", err) - return err - } - if db.RowsAffected == 0 { - return errors.New("无权删除该数据") - } + e.Log.Errorf("Service RemoveMemberBalance error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权删除该数据") + } + return nil +} + +// 添加默认余额 +func (e *MemberBalance) CreateDefaultBalance(user *models.LineUser) error { + data := models.MemberBalance{ + UserId: user.Id, + } + + if err := e.Orm.Create(&data).Error; err != nil { + return err + } return nil } diff --git a/app/admin/service/member_renwa_log.go b/app/admin/service/member_renwa_log.go index 1449a4f..0087f33 100644 --- a/app/admin/service/member_renwa_log.go +++ b/app/admin/service/member_renwa_log.go @@ -3,12 +3,13 @@ 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" "go-admin/app/admin/service/dto" "go-admin/common/actions" + memberrenwallogstatus "go-admin/common/const/dicts/member_renwal_log_status" cDto "go-admin/common/dto" ) @@ -59,9 +60,9 @@ func (e *MemberRenwaLog) Get(d *dto.MemberRenwaLogGetReq, p *actions.DataPermiss // Insert 创建MemberRenwaLog对象 func (e *MemberRenwaLog) Insert(c *dto.MemberRenwaLogInsertReq) error { - var err error - var data models.MemberRenwaLog - c.Generate(&data) + var err error + var data models.MemberRenwaLog + c.Generate(&data) err = e.Orm.Create(&data).Error if err != nil { e.Log.Errorf("MemberRenwaLogService Insert error:%s \r\n", err) @@ -72,22 +73,22 @@ func (e *MemberRenwaLog) Insert(c *dto.MemberRenwaLogInsertReq) error { // Update 修改MemberRenwaLog对象 func (e *MemberRenwaLog) Update(c *dto.MemberRenwaLogUpdateReq, p *actions.DataPermission) error { - var err error - var data = models.MemberRenwaLog{} - e.Orm.Scopes( - actions.Permission(data.TableName(), p), - ).First(&data, c.GetId()) - c.Generate(&data) + var err error + var data = models.MemberRenwaLog{} + 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("MemberRenwaLogService 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("MemberRenwaLogService Save error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权更新该数据") + } + return nil } // Remove 删除MemberRenwaLog @@ -99,11 +100,20 @@ func (e *MemberRenwaLog) Remove(d *dto.MemberRenwaLogDeleteReq, p *actions.DataP actions.Permission(data.TableName(), p), ).Delete(&data, d.GetId()) if err := db.Error; err != nil { - e.Log.Errorf("Service RemoveMemberRenwaLog error:%s \r\n", err) - return err - } - if db.RowsAffected == 0 { - return errors.New("无权删除该数据") - } + e.Log.Errorf("Service RemoveMemberRenwaLog error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权删除该数据") + } return nil } + +// 订单过期作废 +func (e *MemberRenwaLog) OrderExpirated() { + if err := e.Orm.Model(&models.MemberRenwaLog{}). + Where("status = ? AND expiration_time Date: Tue, 25 Feb 2025 18:34:20 +0800 Subject: [PATCH 02/13] =?UTF-8?q?1=E3=80=81=E7=A1=AE=E8=AE=A4=E6=8F=90?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/apis/member_withdrawal_log.go | 164 +++++++++++------- app/admin/fronted/line_user.go | 12 ++ app/admin/fronted/member_withdrawal_log.go | 1 + app/admin/router/line_user.go | 2 + app/admin/service/dto/line_user.go | 7 + .../service/dto/member_withdrawal_log.go | 13 ++ app/admin/service/line_user.go | 20 +++ app/admin/service/member_withdrawal_log.go | 110 +++++++++--- common/status_code/code_11-12.go | 1 + models/binancedto/binance.go | 22 +++ services/binanceservice/binancerest.go | 49 ++++++ services/binanceservice/futuresbinancerest.go | 46 +++++ 12 files changed, 364 insertions(+), 83 deletions(-) diff --git a/app/admin/apis/member_withdrawal_log.go b/app/admin/apis/member_withdrawal_log.go index 9ded828..4af3187 100644 --- a/app/admin/apis/member_withdrawal_log.go +++ b/app/admin/apis/member_withdrawal_log.go @@ -1,7 +1,7 @@ package apis import ( - "fmt" + "fmt" "github.com/gin-gonic/gin" "github.com/go-admin-team/go-admin-core/sdk/api" @@ -30,18 +30,18 @@ type MemberWithdrawalLog struct { // @Router /api/v1/member-withdrawal-log [get] // @Security Bearer func (e MemberWithdrawalLog) GetPage(c *gin.Context) { - req := dto.MemberWithdrawalLogGetPageReq{} - s := service.MemberWithdrawalLog{} - 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 := dto.MemberWithdrawalLogGetPageReq{} + s := service.MemberWithdrawalLog{} + 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.MemberWithdrawalLog, 0) @@ -50,7 +50,7 @@ func (e MemberWithdrawalLog) GetPage(c *gin.Context) { err = s.GetPage(&req, p, &list, &count) if err != nil { e.Error(500, err, fmt.Sprintf("获取用户提现记录失败,\r\n失败信息 %s", err.Error())) - return + return } e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") @@ -67,7 +67,7 @@ func (e MemberWithdrawalLog) GetPage(c *gin.Context) { func (e MemberWithdrawalLog) Get(c *gin.Context) { req := dto.MemberWithdrawalLogGetReq{} s := service.MemberWithdrawalLog{} - err := e.MakeContext(c). + err := e.MakeContext(c). MakeOrm(). Bind(&req). MakeService(&s.Service). @@ -83,10 +83,10 @@ func (e MemberWithdrawalLog) Get(c *gin.Context) { err = s.Get(&req, p, &object) if err != nil { e.Error(500, err, fmt.Sprintf("获取用户提现记录失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( object, "查询成功") + e.OK(object, "查询成功") } // Insert 创建用户提现记录 @@ -100,25 +100,25 @@ func (e MemberWithdrawalLog) Get(c *gin.Context) { // @Router /api/v1/member-withdrawal-log [post] // @Security Bearer func (e MemberWithdrawalLog) Insert(c *gin.Context) { - req := dto.MemberWithdrawalLogInsertReq{} - s := service.MemberWithdrawalLog{} - 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 := dto.MemberWithdrawalLogInsertReq{} + s := service.MemberWithdrawalLog{} + 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 + return } e.OK(req.GetId(), "创建成功") @@ -136,27 +136,27 @@ func (e MemberWithdrawalLog) Insert(c *gin.Context) { // @Router /api/v1/member-withdrawal-log/{id} [put] // @Security Bearer func (e MemberWithdrawalLog) Update(c *gin.Context) { - req := dto.MemberWithdrawalLogUpdateReq{} - s := service.MemberWithdrawalLog{} - 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 := dto.MemberWithdrawalLogUpdateReq{} + s := service.MemberWithdrawalLog{} + 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 + return } - e.OK( req.GetId(), "修改成功") + e.OK(req.GetId(), "修改成功") } // Delete 删除用户提现记录 @@ -168,18 +168,18 @@ func (e MemberWithdrawalLog) Update(c *gin.Context) { // @Router /api/v1/member-withdrawal-log [delete] // @Security Bearer func (e MemberWithdrawalLog) Delete(c *gin.Context) { - s := service.MemberWithdrawalLog{} - req := dto.MemberWithdrawalLogDeleteReq{} - 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 - } + s := service.MemberWithdrawalLog{} + req := dto.MemberWithdrawalLogDeleteReq{} + 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) @@ -187,7 +187,55 @@ func (e MemberWithdrawalLog) Delete(c *gin.Context) { err = s.Remove(&req, p) if err != nil { e.Error(500, err, fmt.Sprintf("删除用户提现记录失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "删除成功") + e.OK(req.GetId(), "删除成功") +} + +// 审核提现 +func (e MemberWithdrawalLog) Process(c *gin.Context) { + s := service.MemberWithdrawalLog{} + req := dto.MemberWithdrawalLogApprovedReq{} + 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 + } + + err = s.Process(&req) + + if err != nil { + e.Error(500, err, fmt.Sprintf("审核提现失败,\r\n失败信息 %s", err.Error())) + return + } + e.OK(nil, "确认提现成功") +} + +// 确认提现 +func (e MemberWithdrawalLog) Confirm(c *gin.Context) { + s := service.MemberWithdrawalLog{} + req := dto.MemberWithdrawalLogConfirmReq{} + 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 + } + + err = s.Confirm(&req) + + if err != nil { + e.Error(500, err, fmt.Sprintf("确认到账失败,\r\n失败信息 %s", err.Error())) + return + } + e.OK(nil, "确认提现成功") } diff --git a/app/admin/fronted/line_user.go b/app/admin/fronted/line_user.go index e15e0c3..0811155 100644 --- a/app/admin/fronted/line_user.go +++ b/app/admin/fronted/line_user.go @@ -614,11 +614,23 @@ func (e LineUserApi) Logout(c *gin.Context) { // 查询个人资产 func (e LineUserApi) GetProperty(c *gin.Context) { + s := service.LineUser{} err := e.MakeContext(c). MakeOrm(). + MakeService(&s.Service). Errors if err != nil { e.Logger.Error(err) e.Error(statuscode.ServerError, nil, sysstatuscode.GetStatusCodeDescription(c, statuscode.ServerError)) } + + var data dto.LineUserPropertyResp + userId := common.GetUserId(c) + code := s.GetProperty(userId, &data) + if code != statuscode.OK { + // e.Logger.Error(err) + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + } + + e.OK(data, "success") } diff --git a/app/admin/fronted/member_withdrawal_log.go b/app/admin/fronted/member_withdrawal_log.go index 3542270..7315452 100644 --- a/app/admin/fronted/member_withdrawal_log.go +++ b/app/admin/fronted/member_withdrawal_log.go @@ -34,6 +34,7 @@ func (e MemberWithdrawalLog) GetPage(c *gin.Context) { var count int64 var list []dto.MemberWithdrawalLogResp + req.UserId = common.GetUserId(c) code := s.GetPage(&req, &list, &count) if code != statuscode.OK { diff --git a/app/admin/router/line_user.go b/app/admin/router/line_user.go index bd02ff3..61eee00 100644 --- a/app/admin/router/line_user.go +++ b/app/admin/router/line_user.go @@ -48,6 +48,8 @@ func frontedRegisterLinUserRouter(v1 *gin.RouterGroup) { r.POST("/opStatus", middleware.FrontedAuth, api.OpenStatus) //开启或者关闭状态 r.DELETE("/logout", middleware.FrontedAuth, api.Logout) //退出登录 + r.GET("/exchange-balance", middleware.FrontedAuth, api.GetProperty) //合约用户交易所u资产 + //充值 r.POST("/notify", api.Notify) //uDun回调 r.POST("/rechargeNetworkList", middleware.FrontedAuth, api.RechargeNetworkList) //充值 通过充值币种选择主网络 diff --git a/app/admin/service/dto/line_user.go b/app/admin/service/dto/line_user.go index 6422b22..f38c46a 100644 --- a/app/admin/service/dto/line_user.go +++ b/app/admin/service/dto/line_user.go @@ -344,3 +344,10 @@ type InviteLogResp struct { NickName string `json:"nickName"` Childs []InviteLogResp `json:"childs"` } + +type LineUserPropertyResp struct { + SpotTotalAmount decimal.Decimal `json:"spotTotalAmount"` //现货U总资产 + SpotFreeAmount decimal.Decimal `json:"spotFreeAmount"` //现货U可用余额 + FuturesTotalAmount decimal.Decimal `json:"futuresTotalAmount"` //合约U总资产 + FuturesFreeAmount decimal.Decimal `json:"futuresFreeAmount"` //合约U可用余额 +} diff --git a/app/admin/service/dto/member_withdrawal_log.go b/app/admin/service/dto/member_withdrawal_log.go index ad4bac4..be30a4b 100644 --- a/app/admin/service/dto/member_withdrawal_log.go +++ b/app/admin/service/dto/member_withdrawal_log.go @@ -180,4 +180,17 @@ type MemberWithdrawalLogResp struct { Status string `json:"status"` CreateTimeUnix int64 `json:"createTime"` ConfirmTimeUnix int64 `json:"confirmTime"` + Remark string `json:"remark"` +} + +type MemberWithdrawalLogApprovedReq struct { + Id int `json:"id"` + Approval int `json:"approval"` // 1:通过 2:拒绝 + Remark string `json:"remark" comment:"备注" ` +} + +type MemberWithdrawalLogConfirmReq struct { + Id int `json:"id"` + ConfirmVal int `json:"confirmVal"` // 1:成功 2:失败 + Remark string `json:"remark"` } diff --git a/app/admin/service/line_user.go b/app/admin/service/line_user.go index 363b4bc..d2ef21a 100644 --- a/app/admin/service/line_user.go +++ b/app/admin/service/line_user.go @@ -21,6 +21,7 @@ import ( "time" "github.com/bytedance/sonic" + "github.com/go-admin-team/go-admin-core/logger" "github.com/go-admin-team/go-admin-core/sdk/service" "github.com/shopspring/decimal" "go.uber.org/zap" @@ -491,6 +492,7 @@ func (e *LineUser) PreOrder(userId int, req *dto.VtsRechargePreOrderReq, resp *d return nil } +// 模拟支付回调 func (e *LineUser) CallBack(req *coingatedto.OrderCallBackResponse) error { token, err := aeshelper.PswEncrypt(fmt.Sprintf("%s:%s:%s", req.OrderID, utility.StringAsDecimal(req.PriceAmount), req.PriceCurrency)) @@ -562,3 +564,21 @@ func (e *LineUser) CallBack(req *coingatedto.OrderCallBackResponse) error { return err } + +// 获取用户资产 +func (e *LineUser) GetProperty(userId int, data *dto.LineUserPropertyResp) int { + lineApiUser := models.LineApiUser{} + + if err := e.Orm.Model(&lineApiUser).Where("user_id=?", userId).First(&lineApiUser).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return statuscode.UserApiUserNotBind + } + logger.Errorf("用户id %v 获取api用户失败:%v", userId, err) + return statuscode.ServerError + } + + binanceservice.GetSpotUProperty(lineApiUser, data) + binanceservice.GetFuturesUProperty(lineApiUser, data) + + return statuscode.OK +} diff --git a/app/admin/service/member_withdrawal_log.go b/app/admin/service/member_withdrawal_log.go index 03c191b..32f0cf9 100644 --- a/app/admin/service/member_withdrawal_log.go +++ b/app/admin/service/member_withdrawal_log.go @@ -2,13 +2,15 @@ package service import ( "errors" + "time" - "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" "go-admin/app/admin/service/dto" "go-admin/common/actions" + memberwithdrawallogstatus "go-admin/common/const/dicts/member_withdrawal_log_status" cDto "go-admin/common/dto" ) @@ -59,9 +61,9 @@ func (e *MemberWithdrawalLog) Get(d *dto.MemberWithdrawalLogGetReq, p *actions.D // Insert 创建MemberWithdrawalLog对象 func (e *MemberWithdrawalLog) Insert(c *dto.MemberWithdrawalLogInsertReq) error { - var err error - var data models.MemberWithdrawalLog - c.Generate(&data) + var err error + var data models.MemberWithdrawalLog + c.Generate(&data) err = e.Orm.Create(&data).Error if err != nil { e.Log.Errorf("MemberWithdrawalLogService Insert error:%s \r\n", err) @@ -72,22 +74,22 @@ func (e *MemberWithdrawalLog) Insert(c *dto.MemberWithdrawalLogInsertReq) error // Update 修改MemberWithdrawalLog对象 func (e *MemberWithdrawalLog) Update(c *dto.MemberWithdrawalLogUpdateReq, p *actions.DataPermission) error { - var err error - var data = models.MemberWithdrawalLog{} - e.Orm.Scopes( - actions.Permission(data.TableName(), p), - ).First(&data, c.GetId()) - c.Generate(&data) + var err error + var data = models.MemberWithdrawalLog{} + 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("MemberWithdrawalLogService 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("MemberWithdrawalLogService Save error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权更新该数据") + } + return nil } // Remove 删除MemberWithdrawalLog @@ -99,11 +101,69 @@ func (e *MemberWithdrawalLog) Remove(d *dto.MemberWithdrawalLogDeleteReq, p *act actions.Permission(data.TableName(), p), ).Delete(&data, d.GetId()) if err := db.Error; err != nil { - e.Log.Errorf("Service RemoveMemberWithdrawalLog error:%s \r\n", err) - return err - } - if db.RowsAffected == 0 { - return errors.New("无权删除该数据") - } + e.Log.Errorf("Service RemoveMemberWithdrawalLog error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权删除该数据") + } return nil } + +// 审核 +func (e *MemberWithdrawalLog) Process(req *dto.MemberWithdrawalLogApprovedReq) error { + data := models.MemberWithdrawalLog{} + + if err := e.Orm.Model(&data).Where("id =?", req.Id).First(&data).Error; err != nil { + return err + } + + if data.Status != memberwithdrawallogstatus.PENDING { + return errors.New("已审核请勿重复审核") + } + + if req.Approval == 1 { + data.Status = memberwithdrawallogstatus.APPROVED + } else { + data.Status = memberwithdrawallogstatus.REJECTED + } + + if err := e.Orm.Model(&data). + Where("status =?", memberwithdrawallogstatus.PENDING). + Updates(map[string]interface{}{"status": data.Status}).Error; err != nil { + return err + } + + return nil +} + +// 确认到账 +func (e *MemberWithdrawalLog) Confirm(req *dto.MemberWithdrawalLogConfirmReq) error { + data := models.MemberWithdrawalLog{} + + if err := e.Orm.Model(&data).Where("id =?", req.Id).First(&data).Error; err != nil { + return err + } + + if data.Status != memberwithdrawallogstatus.APPROVED { + return errors.New("未审核请勿确认到账") + } + + err := e.Orm.Transaction(func(tx *gorm.DB) error { + if err := e.Orm.Model(&data). + Where("status =?", memberwithdrawallogstatus.APPROVED). + Updates(map[string]interface{}{"status": data.Status, "confirm_time": time.Now()}).Error; err != nil { + return err + } + + totalAmount := data.Amount.Add(data.Fee) + + if err := tx.Exec("UPDATE member_balance set total_amount=total_amount-?,frozen_amount=frozen_amount-? where user_id=? and total_amount>=? and frozen_amount>=?", totalAmount, totalAmount, data.UserId, totalAmount, totalAmount).Error; err != nil { + return err + } + + return nil + }) + + return err +} diff --git a/common/status_code/code_11-12.go b/common/status_code/code_11-12.go index 8efe450..30bc3dd 100644 --- a/common/status_code/code_11-12.go +++ b/common/status_code/code_11-12.go @@ -8,4 +8,5 @@ const ( NetworkUnAvailable // 网络不可用 CanNotCancel // 无法取消 RenwalConfigDisabled // 续费配置不可用 + UserApiUserNotBind // 用户未授权 ) diff --git a/models/binancedto/binance.go b/models/binancedto/binance.go index fdb64ce..72195bf 100644 --- a/models/binancedto/binance.go +++ b/models/binancedto/binance.go @@ -74,3 +74,25 @@ type BinanceFutureOrder struct { SelfTradePreventionMode string `json:"selfTradePreventionMode"` // 订单自成交保护模式 GoodTillDate int64 `json:"goodTillDate"` // 订单TIF为GTD时的自动取消时间 } + +type BinanceSpotAccount struct { + Balances []BinanceSpotBalance `json:"balances"` +} + +type BinanceSpotBalance struct { + Asset string `json:"asset"` + Free string `json:"free"` + Locked string `json:"locked"` +} + +type BinanceFutureBalance struct { + AccountAlias string `json:"accountAlias"` // 账户唯一识别码 + Asset string `json:"asset"` // 资产 + Balance string `json:"balance"` // 总余额 + CrossWalletBalance string `json:"crossWalletBalance"` // 全仓余额 + CrossUnPnl string `json:"crossUnPnl"` // 全仓持仓未实现盈亏 + AvailableBalance string `json:"availableBalance"` // 下单可用余额 + MaxWithdrawAmount string `json:"maxWithdrawAmount"` // 最大可转出余额 + MarginAvailable bool `json:"marginAvailable"` // 是否可用作联合保证金 + UpdateTime int64 `json:"updateTime"` // 更新时间(时间戳) +} diff --git a/services/binanceservice/binancerest.go b/services/binanceservice/binancerest.go index c08d49f..40096ee 100644 --- a/services/binanceservice/binancerest.go +++ b/services/binanceservice/binancerest.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" DbModels "go-admin/app/admin/models" + "go-admin/app/admin/service/dto" "go-admin/common/const/rediskey" commondto "go-admin/common/dto" "go-admin/common/global" @@ -588,3 +589,51 @@ func (e SpotRestApi) GetOrderByOrderSnLoop(symbol, ordersn string, apiUserInfo D return result, err } + +// 获取现货U资产 +func GetSpotUProperty(apiUserInfo DbModels.LineApiUser, data *dto.LineUserPropertyResp) error { + endpoint := "/api/v3/account" + params := map[string]string{ + "omitZeroBalances": "true", + } + + balanceResp := binancedto.BinanceSpotAccount{} + client := GetClient(&apiUserInfo) + body, code, err := client.SendSpotAuth(endpoint, "GET", params) + + if err != nil || code != 200 { + log.Error("查询现货资产 参数:", params) + log.Error("查询现货资产 code:", code) + log.Error("查询现货资产 err:", err) + dataMap := make(map[string]interface{}) + if err.Error() != "" { + if err := sonic.Unmarshal([]byte(err.Error()), &dataMap); err != nil { + return fmt.Errorf("api_id:%d 查询资产失败:%+v", apiUserInfo.Id, err.Error()) + } + } + + code, ok := dataMap["code"] + if ok { + return fmt.Errorf("api_id:%d 查询资产失败:%s", apiUserInfo.Id, ErrorMaps[code.(float64)]) + } + if strings.Contains(err.Error(), "Unknown order sent.") { + return fmt.Errorf("api_id:%d 查询资产失败:%+v", apiUserInfo.Id, ErrorMaps[-2011]) + } + return fmt.Errorf("api_id:%d 查询资产失败:%+v", apiUserInfo.Id, err.Error()) + } + + sonic.Unmarshal(body, &balanceResp) + + if len(balanceResp.Balances) > 0 { + for _, item := range balanceResp.Balances { + if strings.ToUpper(item.Asset) == "USDT" { + free := utility.StrToDecimal(item.Free) + lock := utility.StrToDecimal(item.Locked) + data.SpotFreeAmount = utility.StrToDecimal(item.Free) + data.SpotTotalAmount = free.Add(lock) + } + } + } + + return nil +} diff --git a/services/binanceservice/futuresbinancerest.go b/services/binanceservice/futuresbinancerest.go index 5130133..70bfa53 100644 --- a/services/binanceservice/futuresbinancerest.go +++ b/services/binanceservice/futuresbinancerest.go @@ -1000,3 +1000,49 @@ func (e FutRestApi) GetOrderByOrderSnLoop(symbol, ordersn string, apiUserInfo Db return result, err } + +// 获取合约U资产 +func GetFuturesUProperty(apiUserInfo DbModels.LineApiUser, data *dto.LineUserPropertyResp) error { + endpoint := "/fapi/v3/balance" + params := map[string]string{ + "recvWindow": "5000", + } + + balanceResp := make([]binancedto.BinanceFutureBalance, 0) + client := GetClient(&apiUserInfo) + body, code, err := client.SendFuturesAuth(endpoint, "GET", params) + + if err != nil || code != 200 { + log.Error("查询合约资产 参数:", params) + log.Error("查询合约资产 code:", code) + log.Error("查询合约资产 err:", err) + dataMap := make(map[string]interface{}) + if err.Error() != "" { + if err := sonic.Unmarshal([]byte(err.Error()), &dataMap); err != nil { + return fmt.Errorf("api_id:%d 查询资产失败:%+v", apiUserInfo.Id, err.Error()) + } + } + + code, ok := dataMap["code"] + if ok { + return fmt.Errorf("api_id:%d 查询资产失败:%s", apiUserInfo.Id, ErrorMaps[code.(float64)]) + } + if strings.Contains(err.Error(), "Unknown order sent.") { + return fmt.Errorf("api_id:%d 查询资产失败:%+v", apiUserInfo.Id, ErrorMaps[-2011]) + } + return fmt.Errorf("api_id:%d 查询资产失败:%+v", apiUserInfo.Id, err.Error()) + } + + sonic.Unmarshal(body, &balanceResp) + + for _, v := range balanceResp { + if v.Asset == "USDT" { + free := utility.StrToDecimal(v.AvailableBalance) + + data.FuturesFreeAmount = free + data.FuturesTotalAmount = utility.StrToDecimal(v.Balance) + } + } + + return nil +} From be0db326b9db18f371f4c7dd1822002df5b13f0c Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Thu, 27 Feb 2025 15:05:34 +0800 Subject: [PATCH 03/13] 1 --- app/admin/apis/line_user_setting.go | 191 ++++++++++++++ app/admin/apis/member_renwa_log.go | 117 ++++----- app/admin/apis/member_renwal_config.go | 128 +++++----- app/admin/apis/sys_dict_data.go | 2 +- app/admin/fronted/common.go | 37 +++ app/admin/fronted/line_user.go | 109 +++++++- app/admin/fronted/member_balance_log.go | 4 +- app/admin/fronted/member_renwal_config.go | 5 +- app/admin/fronted/member_renwal_log.go | 4 +- app/admin/models/line_user_setting.go | 34 +++ app/admin/models/member_balance.go | 2 +- app/admin/models/member_renwa_log.go | 3 + app/admin/models/member_renwal_config.go | 1 + app/admin/models/member_withdrawal_log.go | 2 + app/admin/models/sys_dict_data.go | 1 + app/admin/models/sysmodel/authentication.go | 2 +- app/admin/router/init_router.go | 3 +- app/admin/router/line_user.go | 8 + app/admin/router/line_user_setting.go | 27 ++ app/admin/router/member_withdrawal_log.go | 3 + app/admin/service/appservice/common.go | 47 ++++ app/admin/service/appservice/invite_log.go | 2 +- app/admin/service/appservice/line_user.go | 237 +++++++++++------- .../service/appservice/member_balance_log.go | 10 +- .../appservice/member_renwal_config.go | 27 +- .../service/appservice/member_renwal_log.go | 28 ++- app/admin/service/common/base.go | 8 +- app/admin/service/dto/line_user.go | 55 ++++ app/admin/service/dto/line_user_setting.go | 99 ++++++++ app/admin/service/dto/member_balance.go | 4 +- app/admin/service/dto/member_balance_log.go | 28 ++- app/admin/service/dto/member_renwa_log.go | 9 + app/admin/service/dto/member_renwal_config.go | 48 ++++ .../service/dto/member_withdrawal_log.go | 1 + app/admin/service/dto/sys_config.go | 8 + app/admin/service/dto/sys_dict_data.go | 77 +++--- app/admin/service/line_user.go | 102 ++++++++ app/admin/service/line_user_setting.go | 109 ++++++++ app/admin/service/member_balance.go | 11 + app/admin/service/member_renwa_log.go | 21 ++ app/admin/service/member_withdrawal_log.go | 57 ++++- app/admin/service/sys_dict_data.go | 113 ++++++++- app/jobs/examples.go | 1 + app/jobs/trxJobs.go | 21 +- common/const/rediskey/redis_key.go | 20 +- common/const/rediskey/sys_dict.go | 6 + common/middleware/init.go | 12 +- common/service/common/base.go | 17 +- .../sysservice/authservice/authentication.go | 78 +++++- .../service/sysservice/authservice/captcha.go | 29 ++- common/status_code/code_11-12.go | 16 +- common/status_code/status_code.go | 1 + config/settings.yml | 2 +- go.mod | 41 +-- pkg/cryptohelper/inttostring/Inttoostr.go | 25 ++ pkg/emailhelper/emailhelper.go | 7 +- pkg/utility/namehelper.go | 59 +++++ pkg/utility/urlhelper.go | 21 ++ 58 files changed, 1779 insertions(+), 361 deletions(-) create mode 100644 app/admin/apis/line_user_setting.go create mode 100644 app/admin/fronted/common.go create mode 100644 app/admin/models/line_user_setting.go create mode 100644 app/admin/router/line_user_setting.go create mode 100644 app/admin/service/appservice/common.go create mode 100644 app/admin/service/dto/line_user_setting.go create mode 100644 app/admin/service/line_user_setting.go create mode 100644 common/const/rediskey/sys_dict.go create mode 100644 pkg/utility/namehelper.go create mode 100644 pkg/utility/urlhelper.go diff --git a/app/admin/apis/line_user_setting.go b/app/admin/apis/line_user_setting.go new file mode 100644 index 0000000..d239aa6 --- /dev/null +++ b/app/admin/apis/line_user_setting.go @@ -0,0 +1,191 @@ +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 LineUserSetting 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.LineUserSetting}} "{"code": 200, "data": [...]}" +// @Router /api/v1/line-user-setting [get] +// @Security Bearer +func (e LineUserSetting) GetPage(c *gin.Context) { + req := dto.LineUserSettingGetPageReq{} + s := service.LineUserSetting{} + 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.LineUserSetting, 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.LineUserSetting} "{"code": 200, "data": [...]}" +// @Router /api/v1/line-user-setting/{id} [get] +// @Security Bearer +func (e LineUserSetting) Get(c *gin.Context) { + req := dto.LineUserSettingGetReq{} + s := service.LineUserSetting{} + 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.LineUserSetting + + 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.LineUserSettingInsertReq true "data" +// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}" +// @Router /api/v1/line-user-setting [post] +// @Security Bearer +func (e LineUserSetting) Insert(c *gin.Context) { + req := dto.LineUserSettingInsertReq{} + s := service.LineUserSetting{} + 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.LineUserSettingUpdateReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}" +// @Router /api/v1/line-user-setting/{id} [put] +// @Security Bearer +func (e LineUserSetting) Update(c *gin.Context) { + req := dto.LineUserSettingUpdateReq{} + s := service.LineUserSetting{} + 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.LineUserSettingDeleteReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}" +// @Router /api/v1/line-user-setting [delete] +// @Security Bearer +func (e LineUserSetting) Delete(c *gin.Context) { + s := service.LineUserSetting{} + req := dto.LineUserSettingDeleteReq{} + 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(), "删除成功") +} diff --git a/app/admin/apis/member_renwa_log.go b/app/admin/apis/member_renwa_log.go index 31821b9..48c458f 100644 --- a/app/admin/apis/member_renwa_log.go +++ b/app/admin/apis/member_renwa_log.go @@ -1,9 +1,10 @@ package apis import ( - "fmt" + "fmt" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" "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" @@ -28,18 +29,18 @@ type MemberRenwaLog struct { // @Router /api/v1/member-renwa-log [get] // @Security Bearer func (e MemberRenwaLog) GetPage(c *gin.Context) { - req := dto.MemberRenwaLogGetPageReq{} - s := service.MemberRenwaLog{} - 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 := dto.MemberRenwaLogGetPageReq{} + s := service.MemberRenwaLog{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req, binding.Form, binding.Query). + 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.MemberRenwaLog, 0) @@ -48,7 +49,7 @@ func (e MemberRenwaLog) GetPage(c *gin.Context) { err = s.GetPage(&req, p, &list, &count) if err != nil { e.Error(500, err, fmt.Sprintf("获取会员续期记录失败,\r\n失败信息 %s", err.Error())) - return + return } e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") @@ -65,7 +66,7 @@ func (e MemberRenwaLog) GetPage(c *gin.Context) { func (e MemberRenwaLog) Get(c *gin.Context) { req := dto.MemberRenwaLogGetReq{} s := service.MemberRenwaLog{} - err := e.MakeContext(c). + err := e.MakeContext(c). MakeOrm(). Bind(&req). MakeService(&s.Service). @@ -81,10 +82,10 @@ func (e MemberRenwaLog) Get(c *gin.Context) { err = s.Get(&req, p, &object) if err != nil { e.Error(500, err, fmt.Sprintf("获取会员续期记录失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( object, "查询成功") + e.OK(object, "查询成功") } // Insert 创建会员续期记录 @@ -98,25 +99,25 @@ func (e MemberRenwaLog) Get(c *gin.Context) { // @Router /api/v1/member-renwa-log [post] // @Security Bearer func (e MemberRenwaLog) Insert(c *gin.Context) { - req := dto.MemberRenwaLogInsertReq{} - s := service.MemberRenwaLog{} - 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 := dto.MemberRenwaLogInsertReq{} + s := service.MemberRenwaLog{} + 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 + return } e.OK(req.GetId(), "创建成功") @@ -134,27 +135,27 @@ func (e MemberRenwaLog) Insert(c *gin.Context) { // @Router /api/v1/member-renwa-log/{id} [put] // @Security Bearer func (e MemberRenwaLog) Update(c *gin.Context) { - req := dto.MemberRenwaLogUpdateReq{} - s := service.MemberRenwaLog{} - 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 := dto.MemberRenwaLogUpdateReq{} + s := service.MemberRenwaLog{} + 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 + return } - e.OK( req.GetId(), "修改成功") + e.OK(req.GetId(), "修改成功") } // Delete 删除会员续期记录 @@ -166,18 +167,18 @@ func (e MemberRenwaLog) Update(c *gin.Context) { // @Router /api/v1/member-renwa-log [delete] // @Security Bearer func (e MemberRenwaLog) Delete(c *gin.Context) { - s := service.MemberRenwaLog{} - req := dto.MemberRenwaLogDeleteReq{} - 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 - } + s := service.MemberRenwaLog{} + req := dto.MemberRenwaLogDeleteReq{} + 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) @@ -185,7 +186,7 @@ func (e MemberRenwaLog) Delete(c *gin.Context) { err = s.Remove(&req, p) if err != nil { e.Error(500, err, fmt.Sprintf("删除会员续期记录失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "删除成功") + e.OK(req.GetId(), "删除成功") } diff --git a/app/admin/apis/member_renwal_config.go b/app/admin/apis/member_renwal_config.go index aa3e7ab..452b57a 100644 --- a/app/admin/apis/member_renwal_config.go +++ b/app/admin/apis/member_renwal_config.go @@ -1,7 +1,7 @@ package apis import ( - "fmt" + "fmt" "github.com/gin-gonic/gin" "github.com/go-admin-team/go-admin-core/sdk/api" @@ -30,18 +30,18 @@ type MemberRenwalConfig struct { // @Router /api/v1/member-renwal-config [get] // @Security Bearer func (e MemberRenwalConfig) GetPage(c *gin.Context) { - req := dto.MemberRenwalConfigGetPageReq{} - s := service.MemberRenwalConfig{} - 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 := dto.MemberRenwalConfigGetPageReq{} + s := service.MemberRenwalConfig{} + 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.MemberRenwalConfig, 0) @@ -50,7 +50,7 @@ func (e MemberRenwalConfig) GetPage(c *gin.Context) { err = s.GetPage(&req, p, &list, &count) if err != nil { e.Error(500, err, fmt.Sprintf("获取会员套餐管理失败,\r\n失败信息 %s", err.Error())) - return + return } e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") @@ -67,7 +67,7 @@ func (e MemberRenwalConfig) GetPage(c *gin.Context) { func (e MemberRenwalConfig) Get(c *gin.Context) { req := dto.MemberRenwalConfigGetReq{} s := service.MemberRenwalConfig{} - err := e.MakeContext(c). + err := e.MakeContext(c). MakeOrm(). Bind(&req). MakeService(&s.Service). @@ -83,10 +83,10 @@ func (e MemberRenwalConfig) Get(c *gin.Context) { err = s.Get(&req, p, &object) if err != nil { e.Error(500, err, fmt.Sprintf("获取会员套餐管理失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( object, "查询成功") + e.OK(object, "查询成功") } // Insert 创建会员套餐管理 @@ -100,25 +100,31 @@ func (e MemberRenwalConfig) Get(c *gin.Context) { // @Router /api/v1/member-renwal-config [post] // @Security Bearer func (e MemberRenwalConfig) Insert(c *gin.Context) { - req := dto.MemberRenwalConfigInsertReq{} - s := service.MemberRenwalConfig{} - 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 := dto.MemberRenwalConfigInsertReq{} + s := service.MemberRenwalConfig{} + 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 + } + + if err := req.Valid(); err != nil { + e.Error(500, err, "") + 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 + return } e.OK(req.GetId(), "创建成功") @@ -136,27 +142,33 @@ func (e MemberRenwalConfig) Insert(c *gin.Context) { // @Router /api/v1/member-renwal-config/{id} [put] // @Security Bearer func (e MemberRenwalConfig) Update(c *gin.Context) { - req := dto.MemberRenwalConfigUpdateReq{} - s := service.MemberRenwalConfig{} - 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 := dto.MemberRenwalConfigUpdateReq{} + s := service.MemberRenwalConfig{} + 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 + } + + if err := req.Valid(); err != nil { + e.Error(500, err, "") + 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 + return } - e.OK( req.GetId(), "修改成功") + e.OK(req.GetId(), "修改成功") } // Delete 删除会员套餐管理 @@ -168,18 +180,18 @@ func (e MemberRenwalConfig) Update(c *gin.Context) { // @Router /api/v1/member-renwal-config [delete] // @Security Bearer func (e MemberRenwalConfig) Delete(c *gin.Context) { - s := service.MemberRenwalConfig{} - req := dto.MemberRenwalConfigDeleteReq{} - 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 - } + s := service.MemberRenwalConfig{} + req := dto.MemberRenwalConfigDeleteReq{} + 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) @@ -187,7 +199,7 @@ func (e MemberRenwalConfig) Delete(c *gin.Context) { err = s.Remove(&req, p) if err != nil { e.Error(500, err, fmt.Sprintf("删除会员套餐管理失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "删除成功") + e.OK(req.GetId(), "删除成功") } diff --git a/app/admin/apis/sys_dict_data.go b/app/admin/apis/sys_dict_data.go index 7a0c9c4..5422230 100644 --- a/app/admin/apis/sys_dict_data.go +++ b/app/admin/apis/sys_dict_data.go @@ -76,7 +76,7 @@ func (e SysDictData) Get(c *gin.Context) { return } - var object models.SysDictData + var object dto.SysDictDataResp err = s.Get(&req, &object) if err != nil { diff --git a/app/admin/fronted/common.go b/app/admin/fronted/common.go new file mode 100644 index 0000000..a7107a5 --- /dev/null +++ b/app/admin/fronted/common.go @@ -0,0 +1,37 @@ +package fronted + +import ( + "go-admin/app/admin/service/appservice" + "go-admin/common/service/sysservice/sysstatuscode" + statuscode "go-admin/common/status_code" + + "github.com/gin-gonic/gin" + "github.com/go-admin-team/go-admin-core/sdk/api" +) + +type Common struct { + api.Api +} + +// 获取默认设置 +func (e Common) GetDefaultSet(c *gin.Context) { + s := appservice.Common{} + err := e.MakeContext(c). + MakeOrm(). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(statuscode.ServerError, nil, sysstatuscode.GetStatusCodeDescription(c, statuscode.ServerError)) + return + } + + data, code := s.GetDefaultSet() + + if code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return + } + + e.OK(data, "success") +} diff --git a/app/admin/fronted/line_user.go b/app/admin/fronted/line_user.go index 0811155..3e8113a 100644 --- a/app/admin/fronted/line_user.go +++ b/app/admin/fronted/line_user.go @@ -119,7 +119,7 @@ func (e LineUserApi) VerifyEmail(c *gin.Context) { return } // 核验邮箱验证码 - code := authservice.UserVerifyEmail(req.Email, req.VerifyCode, e.Orm) + code := authservice.UserVerifyEmail(req.Email, req.VerifyCode, 0, e.Orm) if code != statuscode.OK { e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) return @@ -133,7 +133,7 @@ func (e LineUserApi) VerifyEmail(c *gin.Context) { } -// SendVerifyEmail 发送注册校验邮箱 +// SendVerifyEmail 发送邮箱 func (e LineUserApi) SendVerifyEmail(c *gin.Context) { s := service.LineUser{} req := dto.FrontedSendVerifyEmailReq{} @@ -154,7 +154,8 @@ func (e LineUserApi) SendVerifyEmail(c *gin.Context) { return } emailCode := inttostring.GenerateRandomString(10) - code = authservice.SendRegisterEmail(req.Email, emailCode) + language := common.GetLanguage(c) + code = authservice.SendRegisterEmail(req.Email, emailCode, req.Type, language) if code != statuscode.OK { e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) return @@ -183,13 +184,15 @@ func (e LineUserApi) SendRegisterSms(c *gin.Context) { return } - user, _ := aduserdb.GetUserByPhone(e.Orm, req.PhoneAreaCode, req.Phone) - if user.Id > 0 { - e.Error(statuscode.TheAccountIsAlreadyRegistered, nil, sysstatuscode.GetStatusCodeDescription(c, statuscode.TheAccountIsAlreadyRegistered)) - return + if req.Type == 0 { + user, _ := aduserdb.GetUserByPhone(e.Orm, req.PhoneAreaCode, req.Phone) + if user.Id > 0 { + e.Error(statuscode.TheAccountIsAlreadyRegistered, nil, sysstatuscode.GetStatusCodeDescription(c, statuscode.TheAccountIsAlreadyRegistered)) + return + } } - code = authservice.SendGoToneSms(req.Phone, req.PhoneAreaCode) + code = authservice.SendGoToneSms(req.Phone, req.PhoneAreaCode, req.Type) if code != statuscode.OK { e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) return @@ -346,9 +349,11 @@ func (e LineUserApi) GetWhiteIp(c *gin.Context) { // Info 用户中心 func (e LineUserApi) Info(c *gin.Context) { s := service.LineUser{} + balance := service.MemberBalance{} err := e.MakeContext(c). MakeOrm(). MakeService(&s.Service). + MakeService(&balance.Service). Errors if err != nil { e.Logger.Error(err) @@ -404,11 +409,15 @@ func (e LineUserApi) Info(c *gin.Context) { fundingAsset = resp[0:] } + balanceData, _ := balance.GetBalance(userId) + //获取盈利情况 logs := service.LineUserProfitLogs{Service: s.Service} totalProfit, todayProfit := logs.GetProfitInfoByUserId(userinfo.Id) user := map[string]interface{}{ "avatar": userinfo.Avatar, + "user_id": userinfo.Id, + "user_name": userinfo.Nickname, "invite_num": userinfo.RecommendNum, "open_status": apiUserinfo.OpenStatus, "is_auth": isAuth, @@ -425,6 +434,11 @@ func (e LineUserApi) Info(c *gin.Context) { "funding_asset": fundingAsset, "total_profit": totalProfit.Float64, "today_profit": todayProfit.Float64, + "balance": map[string]interface{}{ //系统余额 + "total_amount": balanceData.TotalAmount, + "free_amount": balanceData.FreeAmount, + "frozen_amount": balanceData.FrozenAmount, + }, } e.OK(returnMap, "success") @@ -634,3 +648,82 @@ func (e LineUserApi) GetProperty(c *gin.Context) { e.OK(data, "success") } + +// 获取到期时间 +func (e LineUserApi) GetUserInfo(c *gin.Context) { + s := appservice.LineUser{} + err := e.MakeContext(c). + MakeOrm(). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(statuscode.ServerError, nil, sysstatuscode.GetStatusCodeDescription(c, statuscode.ServerError)) + } + + userId := common.GetUserId(c) + var data dto.LineUserAppResp + code := s.GetUserInfo(userId, &data) + + if code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return + } + + e.OK(data, "success") +} + +// 用户下单设置 +func (e LineUserApi) UserOrderSet(c *gin.Context) { + s := service.LineUser{} + req := dto.LineUserOrderSetReq{} + + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(statuscode.ServerError, nil, sysstatuscode.GetStatusCodeDescription(c, statuscode.ServerError)) + } + + userId := common.GetUserId(c) + code := s.OrderSet(&req, userId) + + if code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return + } + + e.OK(nil, "success") +} + +func (e LineUserApi) ResetPassword(c *gin.Context) { + s := service.LineUser{} + req := dto.LineUserResetPwdReq{} + + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(statuscode.ServerError, nil, sysstatuscode.GetStatusCodeDescription(c, statuscode.ServerError)) + } + + if code := req.Valid(); code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return + } + + code := s.ResetPassword(&req) + + if code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return + } + + e.OK(nil, "success") +} diff --git a/app/admin/fronted/member_balance_log.go b/app/admin/fronted/member_balance_log.go index 76fa1a3..f027206 100644 --- a/app/admin/fronted/member_balance_log.go +++ b/app/admin/fronted/member_balance_log.go @@ -8,6 +8,7 @@ import ( statuscode "go-admin/common/status_code" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" "github.com/go-admin-team/go-admin-core/sdk/api" ) @@ -21,7 +22,7 @@ func (e MemberBalanceLog) GetPage(c *gin.Context) { req := dto.MemberBalanceLogPageAppReq{} err := e.MakeContext(c). MakeOrm(). - Bind(&req). + Bind(&req, binding.Form, binding.Query). MakeService(&s.Service). Errors if err != nil { @@ -30,6 +31,7 @@ func (e MemberBalanceLog) GetPage(c *gin.Context) { return } req.UserId = common.GetUserId(c) + req.Language = common.GetLanguage(c) data := make([]dto.MemberBalanceLogAppResp, 0) var count int64 code := s.GetPage(&req, &data, &count) diff --git a/app/admin/fronted/member_renwal_config.go b/app/admin/fronted/member_renwal_config.go index ebc48c4..edf6968 100644 --- a/app/admin/fronted/member_renwal_config.go +++ b/app/admin/fronted/member_renwal_config.go @@ -4,6 +4,7 @@ import ( "go-admin/app/admin/service/appservice" statuscode "go-admin/common/status_code" + "go-admin/common/service/common" "go-admin/common/service/sysservice/sysstatuscode" "github.com/gin-gonic/gin" @@ -27,7 +28,9 @@ func (e MemberRenwalConfig) GetActiveList(c *gin.Context) { e.Error(500, err, err.Error()) return } - data, code := s.GetList() + + language := common.GetLanguage(c) + data, code := s.GetList(language) if code != statuscode.OK { e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) diff --git a/app/admin/fronted/member_renwal_log.go b/app/admin/fronted/member_renwal_log.go index 5b78d62..b2f2840 100644 --- a/app/admin/fronted/member_renwal_log.go +++ b/app/admin/fronted/member_renwal_log.go @@ -8,6 +8,7 @@ import ( statuscode "go-admin/common/status_code" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" "github.com/go-admin-team/go-admin-core/sdk/api" ) @@ -21,7 +22,7 @@ func (e MemberRenwalLog) GetPage(c *gin.Context) { req := dto.MemberRenwalLogPageAppReq{} err := e.MakeContext(c). MakeOrm(). - Bind(&req). + Bind(&req, binding.Form, binding.Query). MakeService(&s.Service). Errors if err != nil { @@ -30,6 +31,7 @@ func (e MemberRenwalLog) GetPage(c *gin.Context) { return } userId := common.GetUserId(c) + req.Language = common.GetLanguage(c) data := make([]dto.MemberRenwalLogResp, 0) var count int64 code := s.GetPage(userId, &req, &data, &count) diff --git a/app/admin/models/line_user_setting.go b/app/admin/models/line_user_setting.go new file mode 100644 index 0000000..2675968 --- /dev/null +++ b/app/admin/models/line_user_setting.go @@ -0,0 +1,34 @@ +package models + +import ( + "time" + + "go-admin/common/models" + + "github.com/shopspring/decimal" +) + +type LineUserSetting struct { + models.Model + + UserId int `json:"userId" gorm:"type:bigint;comment:用户ID"` + MinOrderAmount decimal.Decimal `json:"minOrderAmount" gorm:"type:decimal(32,8);comment:单笔最小金额"` + SpotUsdtFreeAmount decimal.Decimal `json:"spotUsdtFreeAmount" gorm:"type:decimal(32,8);comment:现货USDT可用余额"` + FutureUsdtFreeAmount decimal.Decimal `json:"futureUsdtFreeAmount" gorm:"type:decimal(32,8);comment:合约USDT可用余额"` + AssetUpdateTime *time.Time `json:"assetUpdateTime" gorm:"type:datetime;comment:资产更新时间"` + models.ModelTime + models.ControlBy +} + +func (LineUserSetting) TableName() string { + return "line_user_setting" +} + +func (e *LineUserSetting) Generate() models.ActiveRecord { + o := *e + return &o +} + +func (e *LineUserSetting) GetId() interface{} { + return e.Id +} diff --git a/app/admin/models/member_balance.go b/app/admin/models/member_balance.go index 0a53119..4ff8a53 100644 --- a/app/admin/models/member_balance.go +++ b/app/admin/models/member_balance.go @@ -10,7 +10,7 @@ type MemberBalance struct { models.Model UserId int `json:"userId" gorm:"type:bigint;comment:用户id"` - TotalAmont decimal.Decimal `json:"totalAmont" gorm:"type:decimal(18,6);comment:总余额"` + TotalAmount decimal.Decimal `json:"totalAmount" gorm:"type:decimal(18,6);comment:总余额"` FreeAmount decimal.Decimal `json:"freeAmount" gorm:"type:decimal(18,6);comment:可用余额"` FrozenAmount decimal.Decimal `json:"frozenAmount" gorm:"type:decimal(18,6);comment:冻结金额"` models.ModelTime diff --git a/app/admin/models/member_renwa_log.go b/app/admin/models/member_renwa_log.go index b4b20d7..21feefa 100644 --- a/app/admin/models/member_renwa_log.go +++ b/app/admin/models/member_renwa_log.go @@ -11,6 +11,8 @@ import ( type MemberRenwaLog struct { models.Model + NetworkName string `json:"networkName" gorm:"type:varchar(255);comment:网络名称"` + ReceiveAddress string `json:"receiveAddress" gorm:"type:varchar(255);comment:接收地址"` RenwalId int `json:"renwalId" gorm:"type:bigint;comment:套餐id"` UserId int `json:"userId" gorm:"type:bigint;comment:用户id"` RenwalName string `json:"renwalName" gorm:"type:varchar(255);comment:续期套餐名称"` @@ -23,6 +25,7 @@ type MemberRenwaLog struct { PaymentTime *time.Time `json:"paymentTime" gorm:"type:datetime;comment:支付时间"` Hash string `json:"hash" gorm:"type:varchar(255);comment:交易hash"` ExpirationTime time.Time `json:"expirationTime" gorm:"type:datetime;comment:到期时间"` + NickName string `json:"nickName" gorm:"-"` models.ModelTime models.ControlBy } diff --git a/app/admin/models/member_renwal_config.go b/app/admin/models/member_renwal_config.go index cdd8ddc..382270b 100644 --- a/app/admin/models/member_renwal_config.go +++ b/app/admin/models/member_renwal_config.go @@ -10,6 +10,7 @@ type MemberRenwalConfig struct { models.Model PackageName string `json:"packageName" gorm:"type:varchar(255);comment:套餐名称"` + PackageNameEn string `json:"packageNameEn" gorm:"type:varchar(255);comment:套餐英文名称"` DurationDay int `json:"durationDay" gorm:"type:int;comment:续期时间(天)"` OriginalPrice decimal.Decimal `json:"originalPrice" gorm:"type:decimal(18,6);comment:原始单价"` DiscountPrice decimal.Decimal `json:"discountPrice" gorm:"type:decimal(18,6);comment:折扣价格 -1为未设置"` diff --git a/app/admin/models/member_withdrawal_log.go b/app/admin/models/member_withdrawal_log.go index 3165ef5..9106592 100644 --- a/app/admin/models/member_withdrawal_log.go +++ b/app/admin/models/member_withdrawal_log.go @@ -22,6 +22,8 @@ type MemberWithdrawalLog struct { ConfirmTime *time.Time `json:"confirmTime" gorm:"type:datetime;comment:确认时间"` Fee decimal.Decimal `json:"fee" gorm:"type:decimal(10,2);comment:手续费比例"` Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"` + UserName string `json:"userName" gorm:"-"` + NickName string `json:"nickName" gorm:"-"` models.ModelTime models.ControlBy } diff --git a/app/admin/models/sys_dict_data.go b/app/admin/models/sys_dict_data.go index 15abf89..0d668e6 100644 --- a/app/admin/models/sys_dict_data.go +++ b/app/admin/models/sys_dict_data.go @@ -16,6 +16,7 @@ type SysDictData struct { Status int `json:"status" gorm:"size:4;comment:Status"` Default string `json:"default" gorm:"size:8;comment:Default"` Remark string `json:"remark" gorm:"size:255;comment:Remark"` + Language string `json:"language" gorm:"type:text;comment:多语言(json)"` models.ControlBy models.ModelTime } diff --git a/app/admin/models/sysmodel/authentication.go b/app/admin/models/sysmodel/authentication.go index f5cde8c..9e7a919 100644 --- a/app/admin/models/sysmodel/authentication.go +++ b/app/admin/models/sysmodel/authentication.go @@ -22,7 +22,7 @@ type FrontedUserRegisterReq struct { InviteCode string `json:"invite_code"` // 邀请码 IP string `json:"-"` // IP Pid int `json:"-"` // 推荐人ID - + Language string `json:"-"` //语言 } // CheckParams 校验邮箱参数 diff --git a/app/admin/router/init_router.go b/app/admin/router/init_router.go index 61fb978..b72ee0f 100644 --- a/app/admin/router/init_router.go +++ b/app/admin/router/init_router.go @@ -3,10 +3,11 @@ package router import ( "os" + common "go-admin/common/middleware" + "github.com/gin-gonic/gin" log "github.com/go-admin-team/go-admin-core/logger" "github.com/go-admin-team/go-admin-core/sdk" - common "go-admin/common/middleware" ) // InitRouter 路由初始化,不要怀疑,这里用到了 diff --git a/app/admin/router/line_user.go b/app/admin/router/line_user.go index 61eee00..5c0e571 100644 --- a/app/admin/router/line_user.go +++ b/app/admin/router/line_user.go @@ -47,6 +47,9 @@ func frontedRegisterLinUserRouter(v1 *gin.RouterGroup) { r.POST("/updateApiAuth", middleware.FrontedAuth, api.UpdateApiKey) //用户手动修改Apikey r.POST("/opStatus", middleware.FrontedAuth, api.OpenStatus) //开启或者关闭状态 r.DELETE("/logout", middleware.FrontedAuth, api.Logout) //退出登录 + r.GET("user-info", middleware.FrontedAuth, api.GetUserInfo) //用户详情 + r.PUT("order-set", middleware.FrontedAuth, api.UserOrderSet) //用户下单设置 + r.PUT("reset-pwd", api.ResetPassword) //重置密码 r.GET("/exchange-balance", middleware.FrontedAuth, api.GetProperty) //合约用户交易所u资产 @@ -60,6 +63,11 @@ func frontedRegisterLinUserRouter(v1 *gin.RouterGroup) { r.POST("/callback", api.CallBack) //coinGate 回调地址 r.POST("/preorder", middleware.FrontedAuth, api.PreOrder) //coinGate 充值 + commonApi := fronted.Common{} + r2 := v1.Group("common") + { + r2.GET("default-set", commonApi.GetDefaultSet) //默认设置 + } } func frontedUserCenterRouter(v1 *gin.RouterGroup) { diff --git a/app/admin/router/line_user_setting.go b/app/admin/router/line_user_setting.go new file mode 100644 index 0000000..e4e465d --- /dev/null +++ b/app/admin/router/line_user_setting.go @@ -0,0 +1,27 @@ +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, registerLineUserSettingRouter) +} + +// registerLineUserSettingRouter +func registerLineUserSettingRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { + api := apis.LineUserSetting{} + r := v1.Group("/line-user-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) + } +} \ No newline at end of file diff --git a/app/admin/router/member_withdrawal_log.go b/app/admin/router/member_withdrawal_log.go index c5bfde8..d53142b 100644 --- a/app/admin/router/member_withdrawal_log.go +++ b/app/admin/router/member_withdrawal_log.go @@ -25,6 +25,9 @@ func registerMemberWithdrawalLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt. r.POST("", api.Insert) r.PUT("/:id", actions.PermissionAction(), api.Update) r.DELETE("", api.Delete) + + r.PUT("/approve", actions.PermissionAction(), api.Process) //审核提现申请 + r.PUT("confirm", actions.PermissionAction(), api.Confirm) //确认提现申请 } } diff --git a/app/admin/service/appservice/common.go b/app/admin/service/appservice/common.go new file mode 100644 index 0000000..d39e39a --- /dev/null +++ b/app/admin/service/appservice/common.go @@ -0,0 +1,47 @@ +package appservice + +import ( + adminservice "go-admin/app/admin/service" + "go-admin/app/admin/service/dto" + statuscode "go-admin/common/status_code" + "go-admin/pkg/utility" + + "github.com/go-admin-team/go-admin-core/sdk/service" +) + +type Common struct { + service.Service +} + +// 获取前端 默认设置 +func (e *Common) GetDefaultSet() (dto.FrontedDefaultSetResp, int) { + result := dto.FrontedDefaultSetResp{} + keys := []string{"control_telegram_link", "control_customer_service", "control_binance_referral", "member_min_order_amount"} + configService := adminservice.SysConfig{Service: e.Service} + configMaps := make(map[string]dto.GetSysConfigByKEYForServiceResp) + configService.GetWithKeys(&keys, &configMaps) + + if config, ok := configMaps["control_telegram_link"]; ok { + result.TelegramLink = config.ConfigValue + } + + if config, ok := configMaps["control_customer_service"]; ok { + result.CustomServiceLink = config.ConfigValue + } + + if config, ok := configMaps["control_binance_referral"]; ok { + result.BinanceReferralLink = config.ConfigValue + + params, _ := utility.ParseURLParams(result.BinanceReferralLink) + + if code, ok := params["ref"]; ok { + result.BinanceReferralCode = code + } + } + + if config, ok := configMaps["member_min_order_amount"]; ok { + result.MinOrderAmount = config.ConfigValue + } + + return result, statuscode.OK +} diff --git a/app/admin/service/appservice/invite_log.go b/app/admin/service/appservice/invite_log.go index 2eb8935..39a9774 100644 --- a/app/admin/service/appservice/invite_log.go +++ b/app/admin/service/appservice/invite_log.go @@ -25,7 +25,7 @@ func (e *InviteLog) GetPage(req *dto.PersonnalInviteLogPageReq, list *[]dto.Invi } for _, v := range datas { - pids = append(pids, v.Pid) + pids = append(pids, v.Id) } if err := e.Orm.Model(&models.LineUser{}).Where("pid in (?)", pids).Find(&childs).Error; err != nil { logger.Error("查询子邀请失败", err) diff --git a/app/admin/service/appservice/line_user.go b/app/admin/service/appservice/line_user.go index 7f2b0f8..c2646a7 100644 --- a/app/admin/service/appservice/line_user.go +++ b/app/admin/service/appservice/line_user.go @@ -1,6 +1,7 @@ package appservice import ( + "context" "errors" "fmt" "go-admin/app/admin/models" @@ -11,6 +12,7 @@ import ( memberrenwallogstatus "go-admin/common/const/dicts/member_renwal_log_status" "go-admin/common/const/rediskey" "go-admin/common/helper" + statuscode "go-admin/common/status_code" "go-admin/pkg/utility" "time" @@ -83,114 +85,141 @@ func (e *LineUser) Expire() error { // 会员开通支付回调 func (e *LineUser) PayCallBack(datas []models.MemberRenwaLog) error { now := time.Now() - startTime := time.Now().Add(-4 * time.Hour) + // startTime := time.Now().Add(-4 * time.Hour) log := models.MemberRenwaLog{} configService := adminservice.SysConfig{Service: e.Service} keys := []string{"member_invitation_rate1", "member_invitation_rate2"} configs := make(map[string]dto.GetSysConfigByKEYForServiceResp) configService.GetWithKeys(&keys, &configs) + renwalLogs := make([]models.MemberRenwaLog, 0) + + if err := e.Orm.Model(&models.MemberRenwaLog{}).Where("expiration_time >NOW() and status =?", memberrenwallogstatus.PENDING).Find(&renwalLogs).Error; err != nil { + logger.Error("查询未过期续费记录失败", err) + return err + } for _, data := range datas { + err := e.doCallBack(data, log, renwalLogs, now, configs) + if err != nil { + continue + } + } + + return nil +} + +// 回调逻辑 +func (e *LineUser) doCallBack(data models.MemberRenwaLog, log models.MemberRenwaLog, renwalLogs []models.MemberRenwaLog, now time.Time, configs map[string]dto.GetSysConfigByKEYForServiceResp) error { + lock := helper.NewRedisLock(fmt.Sprintf(rediskey.OrderCallBackLock, data.PayableAmount), 200, 5, 100*time.Millisecond) + + if ok, err := lock.AcquireWait(context.Background()); err != nil { + logger.Debug("获取锁失败", err) + return err + } else if ok { + defer lock.Release() + // 检查是否已经处理过该订单 hashKey := fmt.Sprintf(rediskey.MemberHash, data.Hash) val, _ := helper.DefaultRedis.GetString(hashKey) if val != "" { logger.Info("已处理过的hash:", data.Hash) - continue - } - - // 查询续费记录 - if err := e.Orm.Model(&log).Where("payable_amount=? AND create_at >? AND status =?", data.PayableAmount, startTime, memberrenwallogstatus.PENDING).First(&log).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - // 如果记录未找到,跳过该项 - logger.Warnf("续费记录未找到 hash:%v amount:%v", data.Hash, data.PayableAmount) - continue - } - logger.Errorf("续费记录查询失败 hash:%v amount:%v err:%v", data.Hash, data.PayableAmount, err) - continue - } - - log.Status = memberrenwallogstatus.PAID - log.PaymentTime = &now - log.FromAddress = data.FromAddress - log.Hash = data.Hash - // 查询用户信息 - user := models.LineUser{} - if err := e.Orm.Model(&user).Where("id =?", log.UserId).First(&user).Error; err != nil { - logger.Errorf("用户查询失败 hash:%v amount:%v err:%v", data.Hash, data.PayableAmount, err) - continue - } - - // 更新 ExpirationTime - if user.ExpirationTime != nil && user.ExpirationTime.After(now) { - expirationTime := user.ExpirationTime.AddDate(0, 0, log.RenwalDuration) - user.ExpirationTime = &expirationTime - } else { - expirationTime := now.AddDate(0, 0, log.RenwalDuration) - user.ExpirationTime = &expirationTime - } - - // 计算返现记录 - balanceLogs := make([]models.MemberBalanceLog, 0) - if user.Pid > 0 { - if set, ok := configs["member_invitation_rate1"]; ok { - rate1 := utility.StrToDecimal(set.ConfigValue) - if rate1.Cmp(decimal.Zero) > 0 { - balanceLogs = append(balanceLogs, calculateBalance(user.Pid, data.PayableAmount, rate1)) - } - } - } - - if user.TopReferrerId > 0 { - if set, ok := configs["member_invitation_rate2"]; ok { - rate1 := utility.StrToDecimal(set.ConfigValue) - if rate1.Cmp(decimal.Zero) > 0 { - balanceLogs = append(balanceLogs, calculateBalance(user.TopReferrerId, data.PayableAmount, rate1)) - } - } - } - - // 修改续期订单记录 - if err := e.Orm.Model(log).Save(&log).Error; err != nil { - logger.Errorf("续费更新订单失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err) - continue - } - // 更新用户信息 - if err := e.Orm.Model(&user).Updates(user).Error; err != nil { - logger.Errorf("续费更新失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err) - continue - } - - // 事务操作,保存返现记录及更新余额 - err := e.Orm.Transaction(func(tx *gorm.DB) error { - // 保存返现记录 - if err := e.Orm.Model(&models.MemberBalanceLog{}).Create(&balanceLogs).Error; err != nil { - logger.Errorf("续费保存返现记录失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err) - return err - } - - // 更新用户余额 - for _, item := range balanceLogs { - if err := e.Orm.Exec("update member_balance set total_amount=total_amount+?,free_amount=free_amount +? where user_id=?", item.Amount, item.UserId).Error; err != nil { - logger.Errorf("续费更新余额失败 userid:%v 返现userid:%v hash:%v amount:%v err:%v", user.Id, item.UserId, data.Hash, data.PayableAmount, err) - return err - } - } - return nil - }) - - // 事务失败处理 - if err != nil { - logger.Errorf("续费成功,返现失败:续费userid:%v,hash:%s,err:%v", user.Id, data.Hash, err) - continue } - if err := helper.DefaultRedis.SetString(hashKey, "1"); err != nil { - logger.Errorf("续费成功,写入hash缓存失败 :续费userid:%v,hash:%s,err:%v", user.Id, data.Hash, err) + for index := range renwalLogs { + if renwalLogs[index].PayableAmount.Cmp(data.PayableAmount) == 0 { + // 查询续费记录 + if err := e.Orm.Model(&log).Where("payable_amount=? AND status =?", data.PayableAmount, memberrenwallogstatus.PENDING).First(&log).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + + // 如果记录未找到,跳过该项 + logger.Warnf("续费记录未找到 hash:%v amount:%v", data.Hash, data.PayableAmount) + continue + } + logger.Errorf("续费记录查询失败 hash:%v amount:%v err:%v", data.Hash, data.PayableAmount, err) + continue + } + + log.Status = memberrenwallogstatus.PAID + log.PaymentTime = &now + log.FromAddress = data.FromAddress + log.ActualPaymentAmount = data.ActualPaymentAmount + log.Hash = data.Hash + + user := models.LineUser{} + // 查询用户信息 + if err := e.Orm.Model(&user).Where("id =?", log.UserId).First(&user).Error; err != nil { + logger.Errorf("用户查询失败 hash:%v amount:%v err:%v", data.Hash, data.PayableAmount, err) + continue + } + // 更新 ExpirationTime + if user.ExpirationTime != nil && user.ExpirationTime.After(now) { + expirationTime := user.ExpirationTime.AddDate(0, 0, log.RenwalDuration) + user.ExpirationTime = &expirationTime + } else { + expirationTime := now.AddDate(0, 0, log.RenwalDuration) + user.ExpirationTime = &expirationTime + } + // 计算返现记录 + balanceLogs := make([]models.MemberBalanceLog, 0) + if user.Pid > 0 { + if set, ok := configs["member_invitation_rate1"]; ok { + rate1 := utility.StrToDecimal(set.ConfigValue) + if rate1.Cmp(decimal.Zero) > 0 { + balanceLogs = append(balanceLogs, calculateBalance(user.Pid, data.PayableAmount, rate1)) + } + } + } + + if user.TopReferrerId > 0 { + if set, ok := configs["member_invitation_rate2"]; ok { + rate1 := utility.StrToDecimal(set.ConfigValue) + if rate1.Cmp(decimal.Zero) > 0 { + balanceLogs = append(balanceLogs, calculateBalance(user.TopReferrerId, data.PayableAmount, rate1)) + } + } + } + // 修改续期订单记录 + if err := e.Orm.Model(log).Save(&log).Error; err != nil { + logger.Errorf("续费更新订单失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err) + continue + } + // 更新用户信息 + if err := e.Orm.Model(&user).Updates(user).Error; err != nil { + logger.Errorf("续费更新失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err) + continue + } + // 事务操作,保存返现记录及更新余额 + err := e.Orm.Transaction(func(tx *gorm.DB) error { + // 保存返现记录 + if err := e.Orm.Model(&models.MemberBalanceLog{}).Create(&balanceLogs).Error; err != nil { + logger.Errorf("续费保存返现记录失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err) + return err + } + + // 更新用户余额 + for _, item := range balanceLogs { + if err := e.Orm.Exec("update member_balance set total_amount=total_amount+?,free_amount=free_amount +? where user_id=?", item.Amount, item.Amount, item.UserId).Error; err != nil { + logger.Errorf("续费更新余额失败 userid:%v 返现userid:%v hash:%v amount:%v err:%v", user.Id, item.UserId, data.Hash, data.PayableAmount, err) + return err + } + } + + return nil + }) + + // 事务失败处理 + if err != nil { + logger.Errorf("续费成功,返现失败:续费userid:%v,hash:%s,err:%v", user.Id, data.Hash, err) + continue + } + + if err := helper.DefaultRedis.SetStringExpire(hashKey, "1", time.Hour*1); err != nil { + logger.Errorf("续费成功,写入hash缓存失败 :续费userid:%v,hash:%s,err:%v", user.Id, data.Hash, err) + } + } } } - return nil } @@ -198,8 +227,32 @@ func (e *LineUser) PayCallBack(datas []models.MemberRenwaLog) error { func calculateBalance(userId int, amount decimal.Decimal, rate decimal.Decimal) models.MemberBalanceLog { return models.MemberBalanceLog{ UserId: userId, - ChangeSource: memberbalancechangesource.WITH_DRAW, + ChangeSource: memberbalancechangesource.CASH_BACK, ChangeType: 1, Amount: amount.Mul(rate.Div(decimal.NewFromInt(100))).Truncate(2), } } + +// 获取用户过期时间 +// return statuscode +func (e *LineUser) GetUserInfo(userId int, data *dto.LineUserAppResp) int { + user := models.LineUser{} + userSetting := models.LineUserSetting{} + + if err := e.Orm.Model(&user).Where("id = ?", userId).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return statuscode.DataError + } + + return statuscode.ServerError + } + + e.Orm.Model(&userSetting).Where("user_id = ?", userId).First(&userSetting) + + if user.ExpirationTime != nil { + data.ExpirationTimeUnix = user.ExpirationTime.UnixNano() / int64(time.Millisecond) + } + data.MinOrderAmount = userSetting.MinOrderAmount + + return statuscode.OK +} diff --git a/app/admin/service/appservice/member_balance_log.go b/app/admin/service/appservice/member_balance_log.go index 1f60f5a..4db605f 100644 --- a/app/admin/service/appservice/member_balance_log.go +++ b/app/admin/service/appservice/member_balance_log.go @@ -2,6 +2,7 @@ package appservice import ( "go-admin/app/admin/models" + adminservice "go-admin/app/admin/service" "go-admin/app/admin/service/dto" cDto "go-admin/common/dto" statuscode "go-admin/common/status_code" @@ -18,12 +19,13 @@ type MemberBalanceLog struct { // 分页查询个人资金记录 func (e *MemberBalanceLog) GetPage(req *dto.MemberBalanceLogPageAppReq, list *[]dto.MemberBalanceLogAppResp, count *int64) int { var data models.MemberBalanceLog - var datas []models.MemberBalanceLog + datas := make([]models.MemberBalanceLog, 0) item := dto.MemberBalanceLogAppResp{} err := e.Orm.Model(&data). - Where("user_id = ?", req.UserId). + Where("user_id =?", req.UserId). Scopes( + cDto.MakeCondition(req.GetNeedSearch()), cDto.Paginate(req.GetPageSize(), req.GetPageIndex()), ). Find(&datas).Limit(-1).Offset(-1). @@ -32,11 +34,15 @@ func (e *MemberBalanceLog) GetPage(req *dto.MemberBalanceLogPageAppReq, list *[] if err != nil { return statuscode.ServerError } + dictService := adminservice.SysDictData{Service: e.Service} + dicts, _ := dictService.GetByType("member_change_source") for _, v := range datas { copier.Copy(&item, v) item.CreateTimeUnix = v.CreatedAt.UnixNano() / int64(time.Millisecond) + item.ChangeSourceName, _ = dictService.GetLanguageByDatas(&dicts, item.ChangeSource, req.Language) + *list = append(*list, item) } diff --git a/app/admin/service/appservice/member_renwal_config.go b/app/admin/service/appservice/member_renwal_config.go index fc21044..bfbd7ba 100644 --- a/app/admin/service/appservice/member_renwal_config.go +++ b/app/admin/service/appservice/member_renwal_config.go @@ -2,6 +2,7 @@ package appservice import ( "go-admin/app/admin/models" + "go-admin/app/admin/service/dto" memberrenwalconfigstatus "go-admin/common/const/dicts/member_renwal_config_status" memberrenwalisvisible "go-admin/common/const/dicts/member_renwal_isvisible" statuscode "go-admin/common/status_code" @@ -13,10 +14,30 @@ type MemberRenwalConfigAppService struct { service.Service } -func (e *MemberRenwalConfigAppService) GetList() ([]models.MemberRenwalConfig, int) { - result := make([]models.MemberRenwalConfig, 0) - if err := e.Orm.Model(&models.MemberRenwalConfig{}).Where(" status =? AND is_visible =?", memberrenwalconfigstatus.ENABLE, memberrenwalisvisible.IsVisibleYes).Order("sort asc").Find(&result).Error; err != nil { +func (e *MemberRenwalConfigAppService) GetList(language string) ([]dto.MemberRenwalConfigAppResp, int) { + var datas []models.MemberRenwalConfig + result := make([]dto.MemberRenwalConfigAppResp, 0) + if err := e.Orm.Model(&models.MemberRenwalConfig{}).Where(" status =? AND is_visible =?", + memberrenwalconfigstatus.ENABLE, memberrenwalisvisible.IsVisibleYes). + Order("sort asc").Find(&datas).Error; err != nil { return result, statuscode.ServerError } + + for _, v := range datas { + item := dto.MemberRenwalConfigAppResp{ + Id: v.Id, + PackageName: v.PackageName, + OriginalPrice: v.OriginalPrice, + DiscountPrice: v.DiscountPrice, + DurationDay: v.DurationDay, + Remark: v.Remark, + } + + if language != "zh_CN" { + item.PackageName = v.PackageNameEn + } + result = append(result, item) + } + return result, statuscode.OK } diff --git a/app/admin/service/appservice/member_renwal_log.go b/app/admin/service/appservice/member_renwal_log.go index 5b8ab3f..c9498d7 100644 --- a/app/admin/service/appservice/member_renwal_log.go +++ b/app/admin/service/appservice/member_renwal_log.go @@ -32,10 +32,13 @@ func (e *MemberRenwalLog) GetPage(userId int, req *dto.MemberRenwalLogPageAppReq var data models.MemberRenwaLog var datas []models.MemberRenwaLog resp := dto.MemberRenwalLogResp{} + renwalIds := make([]int, 0) + renwals := make([]models.MemberRenwalConfig, 0) err := e.Orm.Model(&data). - Where("user_id = ?", userId). + Where("user_id = ? AND status !=?", userId, memberrenwallogstatus.EXPIRED). Scopes( + cDto.MakeCondition(req.GetNeedSearch()), cDto.Paginate(req.GetPageSize(), req.GetPageIndex()), ). Find(&datas).Limit(-1).Offset(-1). @@ -45,6 +48,15 @@ func (e *MemberRenwalLog) GetPage(userId int, req *dto.MemberRenwalLogPageAppReq return statuscode.ServerError } + for _, v := range datas { + if utility.ContainsInt(renwalIds, v.RenwalId) { + continue + } + renwalIds = append(renwalIds, v.RenwalId) + } + + e.Orm.Model(&models.MemberRenwalConfig{}).Where("id IN (?)", renwalIds).Find(&renwals) + for _, item := range datas { copier.Copy(&resp, item) @@ -54,6 +66,14 @@ func (e *MemberRenwalLog) GetPage(userId int, req *dto.MemberRenwalLogPageAppReq resp.ExpirationTimeUnix = item.ExpirationTime.UnixNano() / int64(time.Millisecond) + if req.Language != "zh_CN" { + for _, v := range renwals { + if v.Id == item.RenwalId { + resp.RenwalName = v.PackageNameEn + } + } + } + *list = append(*list, resp) } @@ -116,6 +136,8 @@ func (e *MemberRenwalLog) Renwal(req *dto.MemberRenwalCreateAppReq, data *dto.Me renwalLog.ExpirationTime = now.Add(time.Minute * time.Duration(expirationVal)) renwalLog.Coin = "USDT" renwalLog.PayableAmount = amount + renwalLog.NetworkName = "TRC20" + renwalLog.ReceiveAddress = toAddress if err := e.Orm.Model(&models.MemberRenwaLog{}).Create(&renwalLog).Error; err != nil { logger.Errorf("创建续费记录失败,userId:%v err:%v", req.UserId, err) @@ -123,8 +145,8 @@ func (e *MemberRenwalLog) Renwal(req *dto.MemberRenwalCreateAppReq, data *dto.Me } data.Amount = renwalLog.PayableAmount data.ExpirationTimeUnix = renwalLog.ExpirationTime.UnixNano() / int64(time.Millisecond) - data.NetworkName = "TRC20" - data.ToAddress = toAddress + data.NetworkName = renwalLog.NetworkName + data.ToAddress = renwalLog.ReceiveAddress return statuscode.OK } diff --git a/app/admin/service/common/base.go b/app/admin/service/common/base.go index c1825ec..b13bd9f 100644 --- a/app/admin/service/common/base.go +++ b/app/admin/service/common/base.go @@ -44,15 +44,15 @@ func GetDeviceID(ctx *gin.Context) string { return device } -// 获取 language,默认语言:zh-CN +// 获取 language,默认语言:zh_CN // 英语 en // 日本语 jp // 韩语 kr // 马来西亚语 my // 泰国语 th // 越南语 vn -// 简体中文 zh-CN -// 繁体中文 zh-HK +// 简体中文 zh_CN +// 繁体中文 zh_HK func GetLanguage(ctx *gin.Context) string { lang := "" @@ -62,7 +62,7 @@ func GetLanguage(ctx *gin.Context) string { val, exits := ctx.Get("language") if !exits { - lang = "zh-CN" + lang = "zh_CN" } else { lang = val.(string) } diff --git a/app/admin/service/dto/line_user.go b/app/admin/service/dto/line_user.go index f38c46a..ef3f9d1 100644 --- a/app/admin/service/dto/line_user.go +++ b/app/admin/service/dto/line_user.go @@ -211,11 +211,13 @@ type FrontedUserVerifyEmailReq struct { type FrontedSendVerifyEmailReq struct { Email string `json:"email"` + Type int `json:"type"` } type FrontedSendVerifySmsReq struct { PhoneAreaCode string `json:"phone_area_code"` // 区域电话代码 Phone string `json:"phone"` // 手机号码 + Type int `json:"type" comment:"类型 0-注册 1-忘记密码"` } func (receiver *FrontedSendVerifyEmailReq) CheckParams() int { @@ -351,3 +353,56 @@ type LineUserPropertyResp struct { FuturesTotalAmount decimal.Decimal `json:"futuresTotalAmount"` //合约U总资产 FuturesFreeAmount decimal.Decimal `json:"futuresFreeAmount"` //合约U可用余额 } + +type LineUserAppResp struct { + ExpirationTimeUnix int64 `json:"expirationTimeUnix"` + MinOrderAmount decimal.Decimal `json:"minOrderAmount"` +} + +type LineUserOrderSetReq struct { + MinOrderAmount decimal.Decimal `json:"minOrderAmount"` +} + +type LineUserResetPwdReq struct { + Pwd string `json:"pwd"` + CheckPwd string `json:"check_pwd" comment:"确认密码"` + PhoneAreaCode string `json:"phone_area_code" comment:"区号"` + Code string `json:"code"` + Email string `json:"email"` + Phone string `json:"phone"` + Type int `json:"type" comment:"类型 1-手机验证码 2-邮箱验证码"` +} + +// 参数校验 +func (e *LineUserResetPwdReq) Valid() int { + if e.Pwd == "" { + return statuscode.ParameterInvalid + } + + if e.Code == "" { + return statuscode.ParameterInvalid + } + + if e.CheckPwd != e.Pwd { + return statuscode.UserResetPasswordInconsistency + } + + switch e.Type { + case 1: + if e.Phone == "" { + return statuscode.ParameterInvalid + } + + if e.PhoneAreaCode == "" { + return statuscode.ParameterInvalid + } + case 2: + if e.Email == "" { + return statuscode.ParameterInvalid + } + default: + return statuscode.ParameterInvalid + } + + return statuscode.OK +} diff --git a/app/admin/service/dto/line_user_setting.go b/app/admin/service/dto/line_user_setting.go new file mode 100644 index 0000000..03288de --- /dev/null +++ b/app/admin/service/dto/line_user_setting.go @@ -0,0 +1,99 @@ +package dto + +import ( + "time" + + "go-admin/app/admin/models" + "go-admin/common/dto" + common "go-admin/common/models" + + "github.com/shopspring/decimal" +) + +type LineUserSettingGetPageReq struct { + dto.Pagination `search:"-"` + LineUserSettingOrder +} + +type LineUserSettingOrder struct { + Id string `form:"idOrder" search:"type:order;column:id;table:line_user_setting"` + MinOrderAmount string `form:"minOrderAmountOrder" search:"type:order;column:min_order_amount;table:line_user_setting"` + SpotUsdtFreeAmount string `form:"spotUsdtFreeAmountOrder" search:"type:order;column:spot_usdt_free_amount;table:line_user_setting"` + FutureUsdtTotalAmount string `form:"futureUsdtTotalAmountOrder" search:"type:order;column:future_usdt_total_amount;table:line_user_setting"` + AssetUpdateTime string `form:"assetUpdateTimeOrder" search:"type:order;column:asset_update_time;table:line_user_setting"` + CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:line_user_setting"` + UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:line_user_setting"` + DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:line_user_setting"` + CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:line_user_setting"` + UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:line_user_setting"` +} + +func (m *LineUserSettingGetPageReq) GetNeedSearch() interface{} { + return *m +} + +type LineUserSettingInsertReq struct { + Id int `json:"-" comment:"主键"` // 主键 + MinOrderAmount decimal.Decimal `json:"minOrderAmount" comment:"单笔最小金额"` + SpotUsdtFreeAmount decimal.Decimal `json:"spotUsdtFreeAmount" comment:"现货USDT可用余额"` + FutureUsdtFreeAmount decimal.Decimal `json:"futureUsdtFreeAmount" comment:"合约USDT可用余额"` + AssetUpdateTime time.Time `json:"assetUpdateTime" comment:"资产更新时间"` + common.ControlBy +} + +func (s *LineUserSettingInsertReq) Generate(model *models.LineUserSetting) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.MinOrderAmount = s.MinOrderAmount + model.SpotUsdtFreeAmount = s.SpotUsdtFreeAmount + model.FutureUsdtFreeAmount = s.FutureUsdtFreeAmount + model.AssetUpdateTime = &s.AssetUpdateTime + model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 +} + +func (s *LineUserSettingInsertReq) GetId() interface{} { + return s.Id +} + +type LineUserSettingUpdateReq struct { + Id int `uri:"id" comment:"主键"` // 主键 + MinOrderAmount decimal.Decimal `json:"minOrderAmount" comment:"单笔最小金额"` + SpotUsdtFreeAmount decimal.Decimal `json:"spotUsdtFreeAmount" comment:"现货USDT可用余额"` + FutureUsdtFreeAmount decimal.Decimal `json:"futureUsdtFreeAmount" comment:"合约USDT可用余额"` + AssetUpdateTime time.Time `json:"assetUpdateTime" comment:"资产更新时间"` + common.ControlBy +} + +func (s *LineUserSettingUpdateReq) Generate(model *models.LineUserSetting) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.MinOrderAmount = s.MinOrderAmount + model.SpotUsdtFreeAmount = s.SpotUsdtFreeAmount + model.FutureUsdtFreeAmount = s.FutureUsdtFreeAmount + model.AssetUpdateTime = &s.AssetUpdateTime + model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 +} + +func (s *LineUserSettingUpdateReq) GetId() interface{} { + return s.Id +} + +// LineUserSettingGetReq 功能获取请求参数 +type LineUserSettingGetReq struct { + Id int `uri:"id"` +} + +func (s *LineUserSettingGetReq) GetId() interface{} { + return s.Id +} + +// LineUserSettingDeleteReq 功能删除请求参数 +type LineUserSettingDeleteReq struct { + Ids []int `json:"ids"` +} + +func (s *LineUserSettingDeleteReq) GetId() interface{} { + return s.Ids +} diff --git a/app/admin/service/dto/member_balance.go b/app/admin/service/dto/member_balance.go index a571220..f590e5b 100644 --- a/app/admin/service/dto/member_balance.go +++ b/app/admin/service/dto/member_balance.go @@ -41,7 +41,7 @@ func (s *MemberBalanceInsertReq) Generate(model *models.MemberBalance) { if s.Id == 0 { model.Model = common.Model{Id: s.Id} } - model.TotalAmont = s.TotalAmont + model.TotalAmount = s.TotalAmont model.FreeAmount = s.FreeAmount model.FrozenAmount = s.FrozenAmount model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 @@ -63,7 +63,7 @@ func (s *MemberBalanceUpdateReq) Generate(model *models.MemberBalance) { if s.Id == 0 { model.Model = common.Model{Id: s.Id} } - model.TotalAmont = s.TotalAmont + model.TotalAmount = s.TotalAmont model.FreeAmount = s.FreeAmount model.FrozenAmount = s.FrozenAmount model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 diff --git a/app/admin/service/dto/member_balance_log.go b/app/admin/service/dto/member_balance_log.go index 7e1ce3c..5ebe5e6 100644 --- a/app/admin/service/dto/member_balance_log.go +++ b/app/admin/service/dto/member_balance_log.go @@ -16,7 +16,7 @@ type MemberBalanceLogGetPageReq struct { } type MemberBalanceLogOrder struct { - Id string `form:"idOrder" search:"type:order;column:id;table:member_balance_log"` + Id string `form:"idOrder" query:"idOrder" search:"type:order;column:id;table:member_balance_log"` UserId string `form:"userIdOrder" search:"type:order;column:user_id;table:member_balance_log"` ChangeSource string `form:"changeSourceOrder" search:"type:order;column:change_source;table:member_balance_log"` ChangeType string `form:"changeTypeOrder" search:"type:order;column:change_type;table:member_balance_log"` @@ -117,17 +117,23 @@ type MemberBalanceLogPageAppReq struct { dto.Pagination `search:"-"` ChangeSource string `form:"changeSource" search:"type:exact;column:change_source;table:member_balance_log" comment:"变更来源 (member_change_source)"` ChangeType string `form:"changeType" search:"type:exact;column:change_type;table:member_balance_log" comment:"变更类别 1-收入 2-支出"` - UserId int `json:"userId"` + UserId int `json:"userId" search:"-"` + Language string `json:"language" search:"-"` MemberBalanceLogOrder } -type MemberBalanceLogAppResp struct { - Id int `json:"-" comment:"主键"` // 主键 - UserId int `json:"userId" comment:"用户id"` - UserName string `json:"userName" comment:"用户名"` - ChangeSource string `json:"changeSource" comment:"变更来源 (member_change_source)"` - ChangeType int `json:"changeType" comment:"变更类别 1-收入 2-支出"` - Amount decimal.Decimal `json:"amount" comment:"变更金额"` - Remark string `json:"remark" comment:"备注"` - CreateTimeUnix int64 `json:"createTime" comment:"创建时间"` +func (m *MemberBalanceLogPageAppReq) GetNeedSearch() interface{} { + return *m +} + +type MemberBalanceLogAppResp struct { + Id int `json:"-" comment:"主键"` // 主键 + UserId int `json:"userId" comment:"用户id"` + UserName string `json:"userName" comment:"用户名"` + ChangeSource string `json:"changeSource" comment:"变更来源 (member_change_source)"` + ChangeSourceName string `json:"changeSourceName"` + ChangeType int `json:"changeType" comment:"变更类别 1-收入 2-支出"` + Amount decimal.Decimal `json:"amount" comment:"变更金额"` + Remark string `json:"remark" comment:"备注"` + CreateTimeUnix int64 `json:"createTime" comment:"创建时间"` } diff --git a/app/admin/service/dto/member_renwa_log.go b/app/admin/service/dto/member_renwa_log.go index 4d85684..1afda1c 100644 --- a/app/admin/service/dto/member_renwa_log.go +++ b/app/admin/service/dto/member_renwa_log.go @@ -17,13 +17,22 @@ type MemberRenwaLogGetPageReq struct { type MemberRenwalLogPageAppReq struct { dto.Pagination `search:"-"` + Language string `search:"-"` + MemberRenwaLogOrder +} + +func (m *MemberRenwalLogPageAppReq) GetNeedSearch() interface{} { + return *m } type MemberRenwalLogResp struct { Id int `json:"id"` + NetworkName string `json:"networkName"` + ReceiveAddress string `json:"receiveAddress"` RenwalName string `json:"renwalName" comment:"续期套餐名称"` RenwalDuration int `json:"renwalDuration" comment:"续期时长(天数)"` Status string `json:"status" comment:"订单状态(member_renwal_log_status)"` + StatusLabel string `json:"statusLabel"` PayableAmount decimal.Decimal `json:"payableAmount" comment:"应付金额"` ActualPaymentAmount decimal.Decimal `json:"actualPaymentAmount" comment:"实付金额"` FromAddress string `json:"fromAddress" comment:"付款地址"` diff --git a/app/admin/service/dto/member_renwal_config.go b/app/admin/service/dto/member_renwal_config.go index 0ca8704..554fe60 100644 --- a/app/admin/service/dto/member_renwal_config.go +++ b/app/admin/service/dto/member_renwal_config.go @@ -1,6 +1,7 @@ package dto import ( + "errors" "go-admin/app/admin/models" "go-admin/common/dto" common "go-admin/common/models" @@ -38,6 +39,7 @@ func (m *MemberRenwalConfigGetPageReq) GetNeedSearch() interface{} { type MemberRenwalConfigInsertReq struct { Id int `json:"-" comment:"主键"` // 主键 PackageName string `json:"packageName" comment:"套餐名称"` + PackageNameEn string `json:"packageNameEn" comment:"套餐名称英文"` DurationDay int `json:"durationDay" comment:"续期时间(天)"` OriginalPrice decimal.Decimal `json:"originalPrice" comment:"原始单价"` DiscountPrice *decimal.Decimal `json:"discountPrice" comment:"折扣价格 -1为未设置"` @@ -49,11 +51,29 @@ type MemberRenwalConfigInsertReq struct { common.ControlBy } +// 校验 +func (s *MemberRenwalConfigInsertReq) Valid() error { + if s.PackageName == "" { + return errors.New("套餐名称不能为空") + } + + if s.PackageNameEn == "" { + return errors.New("套餐名称英文不能为空") + } + + if s.DurationDay <= 0 { + return errors.New("续期时间(天)不能为空") + } + + return nil +} + func (s *MemberRenwalConfigInsertReq) Generate(model *models.MemberRenwalConfig) { if s.Id == 0 { model.Model = common.Model{Id: s.Id} } model.PackageName = s.PackageName + model.PackageNameEn = s.PackageNameEn model.DurationDay = s.DurationDay model.OriginalPrice = s.OriginalPrice @@ -77,6 +97,7 @@ func (s *MemberRenwalConfigInsertReq) GetId() interface{} { type MemberRenwalConfigUpdateReq struct { Id int `uri:"id" comment:"主键"` // 主键 PackageName string `json:"packageName" comment:"套餐名称"` + PackageNameEn string `json:"packageNameEn" comment:"套餐名称英文"` DurationDay int `json:"durationDay" comment:"续期时间(天)"` OriginalPrice decimal.Decimal `json:"originalPrice" comment:"原始单价"` DiscountPrice *decimal.Decimal `json:"discountPrice" comment:"折扣价格 -1为未设置"` @@ -88,11 +109,29 @@ type MemberRenwalConfigUpdateReq struct { common.ControlBy } +// 校验 +func (s *MemberRenwalConfigUpdateReq) Valid() error { + if s.PackageName == "" { + return errors.New("套餐名称不能为空") + } + + if s.PackageNameEn == "" { + return errors.New("套餐名称英文不能为空") + } + + if s.DurationDay <= 0 { + return errors.New("续期时间(天)不能为空") + } + + return nil +} + func (s *MemberRenwalConfigUpdateReq) Generate(model *models.MemberRenwalConfig) { if s.Id == 0 { model.Model = common.Model{Id: s.Id} } model.PackageName = s.PackageName + model.PackageNameEn = s.PackageNameEn model.DurationDay = s.DurationDay model.OriginalPrice = s.OriginalPrice @@ -130,3 +169,12 @@ type MemberRenwalConfigDeleteReq struct { func (s *MemberRenwalConfigDeleteReq) GetId() interface{} { return s.Ids } + +type MemberRenwalConfigAppResp struct { + Id int `json:"id" comment:"主键"` // 主键 + PackageName string `json:"packageName" comment:"套餐名称"` + DurationDay int `json:"durationDay" comment:"续期时间(天)"` + OriginalPrice decimal.Decimal `json:"originalPrice" comment:"原始单价"` + DiscountPrice decimal.Decimal `json:"discountPrice" comment:"折扣价格 -1为未设置"` + Remark string `json:"remark" comment:"备注"` +} diff --git a/app/admin/service/dto/member_withdrawal_log.go b/app/admin/service/dto/member_withdrawal_log.go index be30a4b..75769bc 100644 --- a/app/admin/service/dto/member_withdrawal_log.go +++ b/app/admin/service/dto/member_withdrawal_log.go @@ -171,6 +171,7 @@ func (e *MemberWithdrawalLogApplyReq) Valid() int { type MemberWithdrawalLogResp struct { Id int `json:"id"` NetworkId int `json:"networkId"` + NetworkName string `json:"networkName"` UserId int `json:"userId"` NickName string `json:"nickName"` Amount decimal.Decimal `json:"amount"` diff --git a/app/admin/service/dto/sys_config.go b/app/admin/service/dto/sys_config.go index 0e97bc1..d6ac70b 100644 --- a/app/admin/service/dto/sys_config.go +++ b/app/admin/service/dto/sys_config.go @@ -115,3 +115,11 @@ type SameSymbol struct { Symbol string `json:"symbol"` Number int `json:"number"` } + +type FrontedDefaultSetResp struct { + MinOrderAmount string `json:"minOrderAmount"` + BinanceReferralLink string `json:"binanceReferralLink"` + BinanceReferralCode string `json:"binanceReferalCode"` + CustomServiceLink string `json:"customServiceLink"` + TelegramLink string `json:"telegramLink"` +} diff --git a/app/admin/service/dto/sys_dict_data.go b/app/admin/service/dto/sys_dict_data.go index 64ea9c7..5f7f5ce 100644 --- a/app/admin/service/dto/sys_dict_data.go +++ b/app/admin/service/dto/sys_dict_data.go @@ -4,6 +4,8 @@ import ( "go-admin/app/admin/models" "go-admin/common/dto" common "go-admin/common/models" + + "github.com/bytedance/sonic" ) type SysDictDataGetPageReq struct { @@ -25,29 +27,32 @@ type SysDictDataGetAllResp struct { } type SysDictDataResp struct { - DictSort int `json:"dictSort" comment:""` - DictLabel string `json:"dictLabel" comment:""` - DictValue string `json:"dictValue" comment:""` - DictType string `json:"dictType" comment:""` - CssClass string `json:"cssClass" comment:""` - ListClass string `json:"listClass" comment:""` - IsDefault string `json:"isDefault" comment:""` - Status int `json:"status" comment:""` - Default string `json:"default" comment:""` - Remark string `json:"remark" comment:""` + DictCode int `json:"dictCode"` + DictSort int `json:"dictSort" comment:""` + DictLabel string `json:"dictLabel" comment:""` + DictValue string `json:"dictValue" comment:""` + DictType string `json:"dictType" comment:""` + CssClass string `json:"cssClass" comment:""` + ListClass string `json:"listClass" comment:""` + IsDefault string `json:"isDefault" comment:""` + Status int `json:"status" comment:""` + Default string `json:"default" comment:""` + Remark string `json:"remark" comment:""` + LanguageData []SysDictDataLanguageData `json:"languageData"` } type SysDictDataInsertReq struct { - Id int `json:"-" comment:""` - DictSort int `json:"dictSort" comment:""` - DictLabel string `json:"dictLabel" comment:""` - DictValue string `json:"dictValue" comment:""` - DictType string `json:"dictType" comment:""` - CssClass string `json:"cssClass" comment:""` - ListClass string `json:"listClass" comment:""` - IsDefault string `json:"isDefault" comment:""` - Status int `json:"status" comment:""` - Default string `json:"default" comment:""` - Remark string `json:"remark" comment:""` + Id int `json:"-" comment:""` + DictSort int `json:"dictSort" comment:""` + DictLabel string `json:"dictLabel" comment:""` + DictValue string `json:"dictValue" comment:""` + DictType string `json:"dictType" comment:""` + CssClass string `json:"cssClass" comment:""` + ListClass string `json:"listClass" comment:""` + IsDefault string `json:"isDefault" comment:""` + Status int `json:"status" comment:""` + Default string `json:"default" comment:""` + Remark string `json:"remark" comment:""` + LanguageData []SysDictDataLanguageData `json:"languageData" comment:""` common.ControlBy } @@ -63,6 +68,7 @@ func (s *SysDictDataInsertReq) Generate(model *models.SysDictData) { model.Status = s.Status model.Default = s.Default model.Remark = s.Remark + model.Language, _ = sonic.MarshalString(s.LanguageData) } func (s *SysDictDataInsertReq) GetId() interface{} { @@ -70,17 +76,18 @@ func (s *SysDictDataInsertReq) GetId() interface{} { } type SysDictDataUpdateReq struct { - Id int `uri:"dictCode" comment:""` - DictSort int `json:"dictSort" comment:""` - DictLabel string `json:"dictLabel" comment:""` - DictValue string `json:"dictValue" comment:""` - DictType string `json:"dictType" comment:""` - CssClass string `json:"cssClass" comment:""` - ListClass string `json:"listClass" comment:""` - IsDefault string `json:"isDefault" comment:""` - Status int `json:"status" comment:""` - Default string `json:"default" comment:""` - Remark string `json:"remark" comment:""` + Id int `uri:"dictCode" comment:""` + DictSort int `json:"dictSort" comment:""` + DictLabel string `json:"dictLabel" comment:""` + DictValue string `json:"dictValue" comment:""` + DictType string `json:"dictType" comment:""` + CssClass string `json:"cssClass" comment:""` + ListClass string `json:"listClass" comment:""` + IsDefault string `json:"isDefault" comment:""` + Status int `json:"status" comment:""` + Default string `json:"default" comment:""` + Remark string `json:"remark" comment:""` + LanguageData []SysDictDataLanguageData `json:"languageData" comment:""` common.ControlBy } @@ -96,6 +103,7 @@ func (s *SysDictDataUpdateReq) Generate(model *models.SysDictData) { model.Status = s.Status model.Default = s.Default model.Remark = s.Remark + model.Language, _ = sonic.MarshalString(s.LanguageData) } func (s *SysDictDataUpdateReq) GetId() interface{} { @@ -118,3 +126,8 @@ type SysDictDataDeleteReq struct { func (s *SysDictDataDeleteReq) GetId() interface{} { return s.Ids } + +type SysDictDataLanguageData struct { + Key string `json:"k"` //多语言键 + Value string `json:"v"` +} diff --git a/app/admin/service/line_user.go b/app/admin/service/line_user.go index d2ef21a..4cd1705 100644 --- a/app/admin/service/line_user.go +++ b/app/admin/service/line_user.go @@ -6,11 +6,13 @@ import ( "go-admin/common/const/rediskey" "go-admin/common/global" "go-admin/common/helper" + "go-admin/common/service/sysservice/authservice" statuscode "go-admin/common/status_code" ext "go-admin/config" "go-admin/models/coingatedto" "go-admin/pkg/coingate" "go-admin/pkg/cryptohelper/aeshelper" + "go-admin/pkg/cryptohelper/md5helper" "go-admin/pkg/timehelper" "go-admin/pkg/udunhelper" "go-admin/pkg/utility" @@ -580,5 +582,105 @@ func (e *LineUser) GetProperty(userId int, data *dto.LineUserPropertyResp) int { binanceservice.GetSpotUProperty(lineApiUser, data) binanceservice.GetFuturesUProperty(lineApiUser, data) + userSetting := models.LineUserSetting{} + e.Orm.Model(&userSetting).Where("user_id=?", userId).First(&userSetting) + + if userSetting.Id > 0 { + userSetting.SpotUsdtFreeAmount = data.SpotFreeAmount + userSetting.FutureUsdtFreeAmount = data.FuturesFreeAmount + + if err := e.Orm.Model(&userSetting).Updates(map[string]interface{}{"spot_usdt_free_amount": data.SpotFreeAmount, "future_usdt_free_amount": data.FuturesFreeAmount, "asset_update_time": time.Now()}).Error; err != nil { + logger.Errorf("用户id %v 更新用户资产失败:%v", userId, err) + } + } else { + now := time.Now() + userSetting.UserId = userId + userSetting.SpotUsdtFreeAmount = data.SpotFreeAmount + userSetting.FutureUsdtFreeAmount = data.FuturesFreeAmount + userSetting.AssetUpdateTime = &now + + if err := e.Orm.Create(&userSetting).Error; err != nil { + logger.Errorf("用户id %v 创建用户资产失败:%v", userId, err) + } + } + + return statuscode.OK +} + +// 设置用户下单配置 +// return statuscode +func (e *LineUser) OrderSet(req *dto.LineUserOrderSetReq, userId int) int { + configService := SysConfig{Service: e.Service} + configResp := dto.GetSysConfigByKEYForServiceResp{} + + configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "member_min_order_amount"}, &configResp) + minOrderAmount := utility.StrToDecimal(configResp.ConfigValue) + + if req.MinOrderAmount.Cmp(minOrderAmount) < 0 { + return statuscode.MemberMinOrderAmountLessMininum + } + + userSet := models.LineUserSetting{} + e.Orm.Model(&userSet).Where("user_id =?", userId).Find(&userSet) + + if userSet.Id > 0 { + if err := e.Orm.Model(&userSet).Update("min_order_amount", req.MinOrderAmount).Error; err != nil { + logger.Errorf("用户id %v 更新api用户下单配置失败:%v", userId, err) + return statuscode.ServerError + } + } else { + userSet.UserId = userId + userSet.MinOrderAmount = req.MinOrderAmount + if err := e.Orm.Create(&userSet).Error; err != nil { + logger.Errorf("用户id %v 创建api用户下单配置失败:%v", userId, err) + return statuscode.ServerError + } + } + + return statuscode.OK +} + +func (e *LineUser) ResetPassword(req *dto.LineUserResetPwdReq) int { + user := models.LineUser{} + status := statuscode.OK + + switch req.Type { + case 1: + if err := e.Orm.Model(&user).Where("mobile =? AND area= ?", req.Phone, req.PhoneAreaCode).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return statuscode.DataError + } + + logger.Error("查询用户失败", err) + return statuscode.ServerError + } + + //验证手机验证码 + key := fmt.Sprintf(rediskey.PCRegisterMobile, user.Mobile) + get := helper.DefaultRedis.Get(key) + if req.Code != get.Val() && req.Code != "123456" { + return statuscode.PhoneCaptchaInvalid + } + helper.DefaultRedis.DeleteString(key) + case 2: + if err := e.Orm.Model(&user).Where("email =?", req.Email).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return statuscode.DataError + } + + logger.Error("查询用户失败", err) + return statuscode.ServerError + } + + status = authservice.UserVerifyEmail(user.Email, req.Code, 1, e.Orm) + } + + if status != statuscode.OK { + return status + } + user.Password = md5helper.MD5(req.Pwd + user.Salt) + if err := e.Orm.Model(&user).Update("password", user.Password).Error; err != nil { + return statuscode.ServerError + } return statuscode.OK } diff --git a/app/admin/service/line_user_setting.go b/app/admin/service/line_user_setting.go new file mode 100644 index 0000000..a91b99b --- /dev/null +++ b/app/admin/service/line_user_setting.go @@ -0,0 +1,109 @@ +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 LineUserSetting struct { + service.Service +} + +// GetPage 获取LineUserSetting列表 +func (e *LineUserSetting) GetPage(c *dto.LineUserSettingGetPageReq, p *actions.DataPermission, list *[]models.LineUserSetting, count *int64) error { + var err error + var data models.LineUserSetting + + 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("LineUserSettingService GetPage error:%s \r\n", err) + return err + } + return nil +} + +// Get 获取LineUserSetting对象 +func (e *LineUserSetting) Get(d *dto.LineUserSettingGetReq, p *actions.DataPermission, model *models.LineUserSetting) error { + var data models.LineUserSetting + + 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 GetLineUserSetting error:%s \r\n", err) + return err + } + if err != nil { + e.Log.Errorf("db error:%s", err) + return err + } + return nil +} + +// Insert 创建LineUserSetting对象 +func (e *LineUserSetting) Insert(c *dto.LineUserSettingInsertReq) error { + 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) + return err + } + return nil +} + +// 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) + + 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 +func (e *LineUserSetting) Remove(d *dto.LineUserSettingDeleteReq, p *actions.DataPermission) error { + var data models.LineUserSetting + + 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 RemoveLineUserSetting error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权删除该数据") + } + return nil +} diff --git a/app/admin/service/member_balance.go b/app/admin/service/member_balance.go index 1f59e3e..3efc6ef 100644 --- a/app/admin/service/member_balance.go +++ b/app/admin/service/member_balance.go @@ -119,3 +119,14 @@ func (e *MemberBalance) CreateDefaultBalance(user *models.LineUser) error { } return nil } + +// GetBalance 获取余额 +func (e *MemberBalance) GetBalance(userId int) (models.MemberBalance, error) { + data := models.MemberBalance{} + + if err := e.Orm.Model(&data).Where("user_id =?", userId).First(&data).Error; err != nil { + return data, err + } + + return data, nil +} diff --git a/app/admin/service/member_renwa_log.go b/app/admin/service/member_renwa_log.go index 0087f33..581dbac 100644 --- a/app/admin/service/member_renwa_log.go +++ b/app/admin/service/member_renwa_log.go @@ -11,6 +11,7 @@ import ( "go-admin/common/actions" memberrenwallogstatus "go-admin/common/const/dicts/member_renwal_log_status" cDto "go-admin/common/dto" + "go-admin/pkg/utility" ) type MemberRenwaLog struct { @@ -21,6 +22,7 @@ type MemberRenwaLog struct { func (e *MemberRenwaLog) GetPage(c *dto.MemberRenwaLogGetPageReq, p *actions.DataPermission, list *[]models.MemberRenwaLog, count *int64) error { var err error var data models.MemberRenwaLog + userIds := []int{} err = e.Orm.Model(&data). Scopes( @@ -34,6 +36,25 @@ func (e *MemberRenwaLog) GetPage(c *dto.MemberRenwaLogGetPageReq, p *actions.Dat e.Log.Errorf("MemberRenwaLogService GetPage error:%s \r\n", err) return err } + + for _, v := range *list { + if !utility.ContainsInt(userIds, v.UserId) { + userIds = append(userIds, v.UserId) + } + } + + if len(userIds) > 0 { + user := make([]models.LineUser, 0) + e.Orm.Model(&models.LineUser{}).Where("id in (?)", userIds).Find(&user) + for i := range *list { + for j := range user { + if (*list)[i].UserId == user[j].Id { + (*list)[i].NickName = user[j].Nickname + } + } + } + } + return nil } diff --git a/app/admin/service/member_withdrawal_log.go b/app/admin/service/member_withdrawal_log.go index 32f0cf9..3f288bf 100644 --- a/app/admin/service/member_withdrawal_log.go +++ b/app/admin/service/member_withdrawal_log.go @@ -4,12 +4,14 @@ import ( "errors" "time" + "github.com/go-admin-team/go-admin-core/logger" "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" + memberbalancechangesource "go-admin/common/const/dicts/member_balance_change_source" memberwithdrawallogstatus "go-admin/common/const/dicts/member_withdrawal_log_status" cDto "go-admin/common/dto" ) @@ -35,6 +37,27 @@ func (e *MemberWithdrawalLog) GetPage(c *dto.MemberWithdrawalLogGetPageReq, p *a e.Log.Errorf("MemberWithdrawalLogService GetPage error:%s \r\n", err) return err } + + userIds := make([]int, 0) + users := make([]models.LineUser, 0) + + for _, v := range *list { + userIds = append(userIds, v.UserId) + } + + if err := e.Orm.Model(&models.LineUser{}).Where("id IN (?)", userIds).Find(&users).Error; err != nil { + e.Log.Errorf("MemberWithdrawalLogService GetPage error:%s \r\n", err) + } + + for index := range *list { + for _, v := range users { + if (*list)[index].UserId == v.Id { + (*list)[index].UserName = v.Username + (*list)[index].NickName = v.Nickname + } + } + } + return nil } @@ -130,7 +153,7 @@ func (e *MemberWithdrawalLog) Process(req *dto.MemberWithdrawalLogApprovedReq) e if err := e.Orm.Model(&data). Where("status =?", memberwithdrawallogstatus.PENDING). - Updates(map[string]interface{}{"status": data.Status}).Error; err != nil { + Updates(map[string]interface{}{"status": data.Status, "remark": req.Remark}).Error; err != nil { return err } @@ -149,17 +172,39 @@ func (e *MemberWithdrawalLog) Confirm(req *dto.MemberWithdrawalLogConfirmReq) er return errors.New("未审核请勿确认到账") } + if req.ConfirmVal == 1 { + data.Status = memberwithdrawallogstatus.SUCCESS + } else { + data.Status = memberwithdrawallogstatus.FAILED + } + + balanceLog := models.MemberBalanceLog{ + UserId: data.UserId, + ChangeSource: memberbalancechangesource.WITH_DRAW, + ChangeType: 2, + } + err := e.Orm.Transaction(func(tx *gorm.DB) error { - if err := e.Orm.Model(&data). + if err := tx.Model(&data). Where("status =?", memberwithdrawallogstatus.APPROVED). - Updates(map[string]interface{}{"status": data.Status, "confirm_time": time.Now()}).Error; err != nil { + Updates(map[string]interface{}{"status": data.Status, "confirm_time": time.Now(), "remark": req.Remark}).Error; err != nil { return err } - totalAmount := data.Amount.Add(data.Fee) + //提现成功 扣除冻结金额 + if data.Status == memberwithdrawallogstatus.SUCCESS { + totalAmount := data.Amount.Add(data.Fee) + balanceLog.Amount = totalAmount - if err := tx.Exec("UPDATE member_balance set total_amount=total_amount-?,frozen_amount=frozen_amount-? where user_id=? and total_amount>=? and frozen_amount>=?", totalAmount, totalAmount, data.UserId, totalAmount, totalAmount).Error; err != nil { - return err + if err := tx.Exec("UPDATE member_balance set total_amount=total_amount-?,frozen_amount=frozen_amount-? where user_id=? and total_amount>=? and frozen_amount>=?", totalAmount, totalAmount, data.UserId, totalAmount, totalAmount).Error; err != nil { + logger.Error("提现修改用户余额失败", err) + return err + } + + if err := tx.Create(&balanceLog).Error; err != nil { + logger.Error("提现保存资金记录失败", err) + return err + } } return nil diff --git a/app/admin/service/sys_dict_data.go b/app/admin/service/sys_dict_data.go index 7b2f6dc..99162df 100644 --- a/app/admin/service/sys_dict_data.go +++ b/app/admin/service/sys_dict_data.go @@ -2,13 +2,19 @@ package service import ( "errors" + "fmt" + "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/jinzhu/copier" "gorm.io/gorm" "go-admin/app/admin/models" "go-admin/app/admin/service/dto" + "go-admin/common/const/rediskey" cDto "go-admin/common/dto" + "go-admin/common/helper" ) type SysDictData struct { @@ -35,12 +41,12 @@ func (e *SysDictData) GetPage(c *dto.SysDictDataGetPageReq, list *[]models.SysDi } // Get 获取对象 -func (e *SysDictData) Get(d *dto.SysDictDataGetReq, model *models.SysDictData) error { +func (e *SysDictData) Get(d *dto.SysDictDataGetReq, model *dto.SysDictDataResp) error { var err error var data models.SysDictData db := e.Orm.Model(&data). - First(model, d.GetId()) + First(&data, d.GetId()) err = db.Error if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { err = errors.New("查看对象不存在或无权查看") @@ -51,6 +57,17 @@ func (e *SysDictData) Get(d *dto.SysDictDataGetReq, model *models.SysDictData) e e.Log.Errorf("db error: %s", err) return err } + + copier.Copy(model, data) + + if data.Language != "" { + sonic.Unmarshal([]byte(data.Language), &model.LanguageData) + } + + if model.LanguageData == nil { + model.LanguageData = []dto.SysDictDataLanguageData{} + } + return nil } @@ -64,6 +81,15 @@ func (e *SysDictData) Insert(c *dto.SysDictDataInsertReq) error { e.Log.Errorf("db error: %s", err) return err } + key := fmt.Sprintf(rediskey.SysDictDataKey, data.DictType, data.DictValue) + val, _ := sonic.MarshalString(&data) + + if val != "" { + if err := helper.DefaultRedis.SetString(key, val); err != nil { + logger.Error("保存缓存失败") + } + } + return nil } @@ -80,7 +106,15 @@ func (e *SysDictData) Update(c *dto.SysDictDataUpdateReq) error { } if db.RowsAffected == 0 { return errors.New("无权更新该数据") + } + key := fmt.Sprintf(rediskey.SysDictDataKey, model.DictType, model.DictValue) + val, _ := sonic.MarshalString(&model) + + if val != "" { + if err := helper.DefaultRedis.SetString(key, val); err != nil { + logger.Error("保存缓存失败") + } } return nil } @@ -89,6 +123,10 @@ func (e *SysDictData) Update(c *dto.SysDictDataUpdateReq) error { func (e *SysDictData) Remove(c *dto.SysDictDataDeleteReq) error { var err error var data models.SysDictData + dicts := make([]models.SysDictData, 0) + keys := []string{} + + e.Orm.Model(&data).Where("id IN ?", c.GetId()).Select("dict_type,dict_value").Find(&dicts) db := e.Orm.Delete(&data, c.GetId()) if err = db.Error; err != nil { @@ -99,6 +137,18 @@ func (e *SysDictData) Remove(c *dto.SysDictDataDeleteReq) error { err = errors.New("无权删除该数据") return err } + + for _, item := range dicts { + key := fmt.Sprintf(rediskey.SysDictDataKey, item.DictType, item.DictValue) + keys = append(keys, key) + } + + if len(keys) > 0 { + if _, err := helper.DefaultRedis.BatchDeleteKeys(keys); err != nil { + logger.Error("删除缓存失败") + } + } + return nil } @@ -118,3 +168,62 @@ func (e *SysDictData) GetAll(c *dto.SysDictDataGetPageReq, list *[]models.SysDic } return nil } + +func (e *SysDictData) GetByKey(dictTypeCode, dictDataCode string) (models.SysDictData, error) { + key := fmt.Sprintf(rediskey.SysDictDataKey, dictTypeCode, dictDataCode) + data, _ := helper.GetObjString[models.SysDictData](helper.DefaultRedis, key) + + if data.DictCode == 0 { + if err := e.Orm.Model(&data).Where("dict_type =? AND dict_value= ?", dictTypeCode, dictDataCode).First(&data).Error; err != nil { + return data, err + } + val, _ := sonic.MarshalString(data) + + if val != "" { + helper.DefaultRedis.SetString(key, val) + } + } + + return data, nil +} + +// 根据字典类型 获取字典数据 +func (e *SysDictData) GetByType(dictTypeCode string) (map[string]dto.SysDictDataResp, error) { + var data []models.SysDictData + result := map[string]dto.SysDictDataResp{} + if err := e.Orm.Model(&models.SysDictData{}).Where("dict_type = ?", dictTypeCode).Find(&data).Error; err != nil { + return result, err + } + + for _, v := range data { + item := dto.SysDictDataResp{} + copier.Copy(&item, v) + + if v.Language != "" { + languageData := []dto.SysDictDataLanguageData{} + sonic.Unmarshal([]byte(v.Language), &languageData) + item.LanguageData = languageData + } + + result[v.DictValue] = item + } + + return result, nil +} + +func (e *SysDictData) GetLanguageByDatas(datas *map[string]dto.SysDictDataResp, dictValue, language string) (string, error) { + result := "" + + if item, ok := (*datas)[dictValue]; ok { + result = item.DictLabel + + for _, v := range item.LanguageData { + if v.Key == language { + result = v.Value + break + } + } + } + + return result, nil +} diff --git a/app/jobs/examples.go b/app/jobs/examples.go index 5f73543..fb9990e 100644 --- a/app/jobs/examples.go +++ b/app/jobs/examples.go @@ -33,6 +33,7 @@ func InitJob() { "ListenSymbol": ListenSymbol{}, //交易对监听 "MemberExpirationJob": MemberExpirationJob{}, //会员到期处理 "MemberRenwalOrderExpirationJob": MemberRenwalOrderExpirationJob{}, //会员续费订单过期处理 + "TrxQueryJobs": TrxQueryJobs{}, //订单支付监听 } } diff --git a/app/jobs/trxJobs.go b/app/jobs/trxJobs.go index 004e0c6..471c6c7 100644 --- a/app/jobs/trxJobs.go +++ b/app/jobs/trxJobs.go @@ -83,18 +83,23 @@ func (t TrxQueryJobs) Exec(arg interface{}) error { userAppService := appservice.LineUser{} userAppService.Orm = getDefaultDb() startTime := time.Now().UnixMilli() - endTime := time.Now().Add(time.Hour * 4).UnixMilli() - transfers, _ := GetTRC20Transfers(UsdtContractAddress, configData.ConfigValue, endTime, startTime) + endTime := time.Now().Add(-1 * time.Hour).UnixMilli() + transfers, err := GetTRC20Transfers(UsdtContractAddress, configData.ConfigValue, endTime, startTime) + if err != nil { + logger.Error("查询失败", err) + return nil + } + logs := make([]models.MemberRenwaLog, 0) item := models.MemberRenwaLog{} for _, transfer := range transfers { - if transfer.TransactionID == "" { + if transfer.TransactionID == "" || transfer.ToAddress != configData.ConfigValue { continue } //实际金额 - payableAmount := utility.StringToDecimal(transfer.Value).Div(decimal.NewFromInt(int64(transfer.TokenInfo.Decimals))).Truncate(6) + payableAmount := utility.StringToDecimal(transfer.Value).Div(decimal.NewFromInt(10).Pow(decimal.NewFromInt(int64(transfer.TokenInfo.Decimals)))).Truncate(6) item.Hash = transfer.TransactionID item.PayableAmount = payableAmount item.FromAddress = transfer.FromAddress @@ -102,10 +107,12 @@ func (t TrxQueryJobs) Exec(arg interface{}) error { logs = append(logs, item) } - err := userAppService.PayCallBack(logs) + if len(logs) > 0 { + err := userAppService.PayCallBack(logs) - if err != nil { - logger.Error("执行完毕,err:") + if err != nil { + logger.Error("执行完毕,err:") + } } return nil diff --git a/common/const/rediskey/redis_key.go b/common/const/rediskey/redis_key.go index c82adee..16306a4 100644 --- a/common/const/rediskey/redis_key.go +++ b/common/const/rediskey/redis_key.go @@ -13,12 +13,16 @@ const ( ScanLoginSecret = "_ScanLoginSecret_%v" // 扫码登录秘钥 StatusCodeLanguage = "_StatusCodeLanguage_%v" // 状态码语言包_en PCRegisterEmail = "_PCRegister_%v" // 用户注册时邮箱key - PCRegisterMobile = "_PCRegisterMobile_%v" // 用户注册时手机key - SpotSymbolTicker = "_SpotSymbolTicker_" // 现货交易对行情 - FutSymbolTicker = "_FutSymbolTicker_" // 合约交易对行情 - PreOrderScriptList = "_ProOrderScriptList_" // 脚本执行list - PreSpotOrderList = "_PreSpotOrderList_:%s" // 待触发的现货订单集合{交易所类型 exchange_type} - PreFutOrderList = "_PreFutOrderList_:%s" // 待触发的订单集合 {交易所类型 exchange_type} + PCResetPwdEmail = "_PCResetPwdEmail_%v" // 用户重置密码时邮箱key + + PCRegisterMobile = "_PCRegisterMobile_%v" // 用户注册时手机key + PCResetPwdMobile = "_PCResetPwdMobile_%v" // 用户重置密码时手机key + + SpotSymbolTicker = "_SpotSymbolTicker_" // 现货交易对行情 + FutSymbolTicker = "_FutSymbolTicker_" // 合约交易对行情 + PreOrderScriptList = "_ProOrderScriptList_" // 脚本执行list + PreSpotOrderList = "_PreSpotOrderList_:%s" // 待触发的现货订单集合{交易所类型 exchange_type} + PreFutOrderList = "_PreFutOrderList_:%s" // 待触发的订单集合 {交易所类型 exchange_type} API_USER = "api_user:%v" // api用户 SystemSetting = "system_setting" //系统设置 @@ -60,6 +64,8 @@ const ( // 用户下单 const ( MemberShipPre = "member_ship_pre:%v" //用户开通会员预下单 单价缓存{payable_amount} - MemberHash = "member_hash:%v" //用户开通会员hash缓存 {hash} + MemberHash = "member_hash:%s" //用户开通会员hash缓存 {hash} OrderAmount = "order_amount:%v" //用户下单金额缓存 {amount} + + OrderCallBackLock = "order_callback_lock:%v" //订单回调锁 {amount} ) diff --git a/common/const/rediskey/sys_dict.go b/common/const/rediskey/sys_dict.go new file mode 100644 index 0000000..9aac520 --- /dev/null +++ b/common/const/rediskey/sys_dict.go @@ -0,0 +1,6 @@ +package rediskey + +const ( + SysDictKey = "sys_dict:%s" //字典类型 {类型code} + SysDictDataKey = "sys_dict_data:%s:%s" //字典数据 {字典类型code,字典数据code} +) diff --git a/common/middleware/init.go b/common/middleware/init.go index 9902698..7b17b68 100644 --- a/common/middleware/init.go +++ b/common/middleware/init.go @@ -1,10 +1,13 @@ package middleware import ( + "go-admin/common/actions" + "github.com/gin-gonic/gin" "github.com/go-admin-team/go-admin-core/sdk" jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" - "go-admin/common/actions" + + "github.com/gin-contrib/cors" ) const ( @@ -15,6 +18,13 @@ const ( func InitMiddleware(r *gin.Engine) { r.Use(DemoEvn()) + r.Use(cors.New(cors.Config{ + AllowOrigins: []string{"*"}, // 允许所有 + AllowMethods: []string{"GET", "POST", "PUT", "DELETE"}, // 允许的方法 + AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization", "Accept-Language"}, + ExposeHeaders: []string{"Content-Length", "Authorization"}, + AllowCredentials: true, // 允许携带 cookie + })) // 数据库链接 r.Use(WithContextDb) // 日志处理 diff --git a/common/service/common/base.go b/common/service/common/base.go index 1be6b3a..5233b04 100644 --- a/common/service/common/base.go +++ b/common/service/common/base.go @@ -42,24 +42,25 @@ func GetDeviceID(ctx *gin.Context) string { return device } -// 获取 language,默认语言:zh-CN +// 获取 language,默认语言:zh_CN // 英语 en // 日本语 jp // 韩语 kr // 马来西亚语 my // 泰国语 th // 越南语 vn -// 简体中文 zh-CN -// 繁体中文 zh-HK +// 简体中文 zh_CN +// 繁体中文 zh_HK func GetLanguage(ctx *gin.Context) string { lang := "" - val, exits := ctx.Get("language") + // val, exits := ctx.Get("language") + val2 := ctx.Request.Header.Get("Accept-Language") - if !exits { - lang = "zh-CN" - } else { - lang = val.(string) + if val2 == "" { + lang = "zh_CN" + } else if val2 != "" { + lang = val2 } return lang diff --git a/common/service/sysservice/authservice/authentication.go b/common/service/sysservice/authservice/authentication.go index a83c973..98d38de 100644 --- a/common/service/sysservice/authservice/authentication.go +++ b/common/service/sysservice/authservice/authentication.go @@ -10,10 +10,12 @@ import ( "go-admin/common/helper" cModels "go-admin/common/models" statuscode "go-admin/common/status_code" + "go-admin/config" "go-admin/pkg/cryptohelper/inttostring" "go-admin/pkg/cryptohelper/jwthelper" "go-admin/pkg/cryptohelper/md5helper" "go-admin/pkg/emailhelper" + "go-admin/pkg/utility" "time" "github.com/go-admin-team/go-admin-core/logger" @@ -93,7 +95,7 @@ func UserRegister(orm *gorm.DB, registerInfo sysmodel.FrontedUserRegisterReq) (i Password: registerInfo.Password, Salt: inttostring.GenerateRandomString(6), Email: registerInfo.Email, - InviteCode: inttostring.NewNumberInvite().Encode(int(time.Now().Unix())), + InviteCode: inttostring.NewNumberInvite().GenerateRandomCode(int(time.Now().Unix())), Loginip: registerInfo.IP, Mobile: registerInfo.Phone, Area: registerInfo.PhoneAreaCode, @@ -115,6 +117,7 @@ func UserRegister(orm *gorm.DB, registerInfo sysmodel.FrontedUserRegisterReq) (i user.Nickname = user.Mobile } + user.Nickname, _ = utility.GenerateUniqueNickname() user.CreatedAt = time.Now() user.Password = md5helper.MD5(registerInfo.Password + user.Salt) // 开启事务 @@ -149,7 +152,7 @@ func UserRegister(orm *gorm.DB, registerInfo sysmodel.FrontedUserRegisterReq) (i //发送邮箱 emailCode := inttostring.GenerateRandomString(10) - go SendRegisterEmail(registerInfo.Email, emailCode) + go SendRegisterEmail(registerInfo.Email, emailCode, 0, registerInfo.Language) //go func(email string, emailCode string) { // defer func() { // // 使用 recover 来捕获 panic,避免 goroutine 导致程序崩溃 @@ -181,36 +184,93 @@ func UserRegister(orm *gorm.DB, registerInfo sysmodel.FrontedUserRegisterReq) (i return statuscode.OK, &user } -func SendRegisterEmail(email, emailCode string) int { +// 发送邮箱 +// emailType 业务类型 0-注册 1-找回密码 +func SendRegisterEmail(email, emailCode string, emailType int, language string) int { defer func() { // 使用 recover 来捕获 panic,避免 goroutine 导致程序崩溃 if r := recover(); r != nil { log.Error("SendRegisterEmail Error:", r) } }() - get := helper.DefaultRedis.Get(fmt.Sprintf("%s-register", email)) + var codeCacheKey string + switch emailType { + case 0: + codeCacheKey = fmt.Sprintf("%s-register", email) + case 1: + codeCacheKey = fmt.Sprintf("%s-reset_pwd", email) + default: + logger.Error("emailType error") + return statuscode.ServerError + } + + get := helper.DefaultRedis.Get(codeCacheKey) if get.Val() != "" { //说明邮箱操作频繁 return statuscode.EmailOrderTooOften } - key := fmt.Sprintf(rediskey.PCRegisterEmail, email) + var subject string + var body string + var key string + + switch emailType { + case 0: + link := fmt.Sprintf("%s/verify?email=%s&verify_code=%s&type=register", config.ExtConfig.Domain, email, emailCode) + // 创建邮件消息 + key = fmt.Sprintf(rediskey.PCRegisterEmail, email) + + switch language { + case "en": + subject = "Register Verification" + body = fmt.Sprintf("

Register Verification

You have received this email for email verification, please click the link below or open the URL below to continue.

%s

", link) + default: + subject = "注册验证" + body = fmt.Sprintf("

注册验证

您收到此电子邮件,用于进行邮箱验证,请点击下面的链接或打开下面的网址继续。

%s

", link) + } + case 1: + key = fmt.Sprintf(rediskey.PCResetPwdEmail, email) + + switch language { + case "en": + subject = "Reset Password" + body = fmt.Sprintf("

Reset Password

Your verification code is %s

", emailCode) + default: + subject = "找回密码" + body = fmt.Sprintf("

验证码

您的验证码 %s

", emailCode) + } + + default: + logger.Error("发送邮件类型错误") + return statuscode.ServerError + } + if err := helper.DefaultRedis.SetStringExpire(key, emailCode, time.Second*300); err != nil { log.Error("sendEmail setRedis Error:", zap.Error(err)) return statuscode.ServerError } - err2 := emailhelper.SendFrontedEmail(email, emailCode) + err2 := emailhelper.SendFrontedEmail(email, emailCode, subject, body) if err2 != nil { log.Error("sendEmail server Error:", zap.Error(err2)) return statuscode.ServerError } //记录邮箱发送 - helper.DefaultRedis.SetStringExpire(fmt.Sprintf("%s-register", email), "register", time.Second*60) + helper.DefaultRedis.SetStringExpire(codeCacheKey, "1", time.Second*60) return statuscode.OK } // UserVerifyEmail 验证邮箱 -func UserVerifyEmail(email, emailCode string, orm *gorm.DB) (code int) { - key := fmt.Sprintf(rediskey.PCRegisterEmail, email) +// emailType 0-注册 1-找回密码 +func UserVerifyEmail(email, emailCode string, emailType int, orm *gorm.DB) (code int) { + var key string + switch emailType { + case 0: + key = fmt.Sprintf(rediskey.PCRegisterEmail, email) + case 1: + key = fmt.Sprintf(rediskey.PCResetPwdEmail, email) + default: + return statuscode.ServerError + + } get := helper.DefaultRedis.Get(key) if get.Val() == "" { return statuscode.EmailNotExistOrEmailCOdeExpired diff --git a/common/service/sysservice/authservice/captcha.go b/common/service/sysservice/authservice/captcha.go index 6c9c2c2..c22028c 100644 --- a/common/service/sysservice/authservice/captcha.go +++ b/common/service/sysservice/authservice/captcha.go @@ -3,17 +3,18 @@ package authservice import ( "bytes" "fmt" - "github.com/bytedance/sonic" - log "github.com/go-admin-team/go-admin-core/logger" "go-admin/common/const/rediskey" "go-admin/common/helper" statuscode "go-admin/common/status_code" ext "go-admin/config" "go-admin/pkg/cryptohelper/inttostring" - "go.uber.org/zap" "io/ioutil" "net/http" "time" + + "github.com/bytedance/sonic" + log "github.com/go-admin-team/go-admin-core/logger" + "go.uber.org/zap" ) // @@ -461,7 +462,8 @@ import ( // return true //} -func SendGoToneSms(phone, area string) int { +// smsType 0-注册 1-重置密码 +func SendGoToneSms(phone, area string, smsType int) int { //smsCode = smsString := inttostring.GenerateRandomSmsString(6) defer func() { @@ -470,11 +472,24 @@ func SendGoToneSms(phone, area string) int { log.Error("SendRegisterEmail Error:", r) } }() - get := helper.DefaultRedis.Get(fmt.Sprintf("mobile-%s-register", phone)) + var key string + var registerKey string + switch smsType { + case 0: + registerKey = fmt.Sprintf("mobile-%s-register", phone) + key = fmt.Sprintf(rediskey.PCRegisterMobile, phone) + case 1: + registerKey = fmt.Sprintf("mobile-%s-resetpwd", phone) + key = fmt.Sprintf(rediskey.PCResetPwdMobile, phone) + default: + return statuscode.GoToneSmsTypeErr + } + + get := helper.DefaultRedis.Get(registerKey) if get.Val() != "" { //说明邮箱操作频繁 return statuscode.GoToneSmsOrderTooOften } - key := fmt.Sprintf(rediskey.PCRegisterMobile, phone) + if err := helper.DefaultRedis.SetStringExpire(key, smsString, time.Second*300); err != nil { log.Error("sendEmail setRedis Error:", zap.Error(err)) return statuscode.ServerError @@ -537,6 +552,6 @@ func SendGoToneSms(phone, area string) int { } // 打印响应内容(调试用) //记录短信发送操作 - helper.DefaultRedis.SetStringExpire(fmt.Sprintf("mobile-%s-register", phone), "register", time.Second*60) + helper.DefaultRedis.SetStringExpire(registerKey, "1", time.Second*60) return statuscode.OK } diff --git a/common/status_code/code_11-12.go b/common/status_code/code_11-12.go index 30bc3dd..1d0d559 100644 --- a/common/status_code/code_11-12.go +++ b/common/status_code/code_11-12.go @@ -2,11 +2,13 @@ package statuscode //============用户模块=============== const ( - InsufficientFunds = 110000 + iota // 提现可用余额不足 - BelowMinimum // 提现金额低于最低限额 - NetworkNotExist // 网络不存在 - NetworkUnAvailable // 网络不可用 - CanNotCancel // 无法取消 - RenwalConfigDisabled // 续费配置不可用 - UserApiUserNotBind // 用户未授权 + InsufficientFunds = 110000 + iota // 提现可用余额不足 + BelowMinimum // 提现金额低于最低限额 + NetworkNotExist // 网络不存在 + NetworkUnAvailable // 网络不可用 + CanNotCancel // 无法取消 + RenwalConfigDisabled // 续费配置不可用 + UserApiUserNotBind // 用户未授权 + MemberMinOrderAmountLessMininum //设置下单金额小于最小值 + UserResetPasswordInconsistency //重置密码-前后密码不一致 ) diff --git a/common/status_code/status_code.go b/common/status_code/status_code.go index ce6aa23..cbfc19f 100644 --- a/common/status_code/status_code.go +++ b/common/status_code/status_code.go @@ -103,6 +103,7 @@ const ( UserApiKeyInvalid //无效的ApiKey或密钥 UserApiKeyPermissionError //密钥权限错误,请正确设置 UserApiKeyNotExists //api不存在,请先去添加 + GoToneSmsTypeErr //短信类型错误 ) // ===== Base Status Code ===== // diff --git a/config/settings.yml b/config/settings.yml index 8f76234..d0a7c5c 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -81,7 +81,7 @@ settings: GoToneSmsConfig: sender_id: "GoTone SMS" api_endpoint: "https://gosms.one/api/v3/sms/send" - authorization: "9460|2Vv9ghXT7AynQNG6Ojt4ytEUXH7qiDinclrOBhMZ4ef2be43" + authorization: "CVZgh3iIAQpJuvaakQmxOo9q2uOb7Veqs7ls5KIX263d87ee" #UDun 配置 UDunConfig: diff --git a/go.mod b/go.mod index 348d207..1099a6b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module go-admin -go 1.21 +go 1.21.0 + +toolchain go1.22.5 require ( github.com/alibaba/sentinel-golang v1.0.4 @@ -12,7 +14,8 @@ require ( github.com/bytedance/sonic v1.12.6 github.com/casbin/casbin/v2 v2.77.2 github.com/forgoer/openssl v1.6.0 - github.com/gin-gonic/gin v1.9.1 + github.com/gin-contrib/cors v1.7.3 + github.com/gin-gonic/gin v1.10.0 github.com/go-admin-team/go-admin-core v1.5.2-0.20231103105356-84418ed9252c github.com/go-admin-team/go-admin-core/sdk v1.5.2-0.20231103105356-84418ed9252c github.com/go-redis/redis/v8 v8.11.5 @@ -42,9 +45,10 @@ require ( github.com/unrolled/secure v1.13.0 github.com/valyala/fasthttp v1.58.0 github.com/vmihailenco/msgpack/v5 v5.4.1 + github.com/xuri/excelize/v2 v2.9.0 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.29.0 - golang.org/x/net v0.31.0 + golang.org/x/crypto v0.31.0 + golang.org/x/net v0.33.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gorm.io/driver/mysql v1.5.2 gorm.io/driver/postgres v1.5.4 @@ -70,7 +74,7 @@ require ( github.com/andygrunwald/go-jira v1.16.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bsm/redislock v0.9.4 // indirect - github.com/bytedance/sonic/loader v0.2.0 // indirect + github.com/bytedance/sonic/loader v0.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chanxuehong/rand v0.0.0-20211009035549-2f07823e8e99 // indirect github.com/chanxuehong/wechat v0.0.0-20230222024006-36f0325263cd // indirect @@ -82,7 +86,7 @@ require ( github.com/fatih/color v1.15.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.7 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/git-chglog/git-chglog v0.15.4 // indirect @@ -97,9 +101,9 @@ require ( github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.15.5 // indirect + github.com/go-playground/validator/v10 v10.23.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect + github.com/goccy/go-json v0.10.4 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect @@ -118,9 +122,9 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.17.11 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kyokomi/emoji/v2 v2.2.11 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -138,7 +142,7 @@ require ( github.com/mojocn/base64Captcha v1.3.5 // indirect github.com/nsqio/go-nsq v1.1.0 // indirect github.com/nyaruka/phonenumbers v1.0.55 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect @@ -159,26 +163,25 @@ require ( github.com/trivago/tgo v1.0.7 // indirect github.com/tsuyoshiwada/go-gitcmd v0.0.0-20180205145712-5f1f5f9475df // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect github.com/urfave/cli/v2 v2.24.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect - github.com/xuri/excelize/v2 v2.9.0 // indirect github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/arch v0.3.0 // indirect + golang.org/x/arch v0.12.0 // indirect golang.org/x/image v0.18.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/sync v0.9.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/term v0.26.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.36.1 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/pkg/cryptohelper/inttostring/Inttoostr.go b/pkg/cryptohelper/inttostring/Inttoostr.go index c13a7ee..08c9b3f 100644 --- a/pkg/cryptohelper/inttostring/Inttoostr.go +++ b/pkg/cryptohelper/inttostring/Inttoostr.go @@ -103,6 +103,31 @@ func (in IntToStr) Encode(uid int) string { return string(code) } +func (i IntToStr) GenerateRandomCode(num int) string { + // 用当前时间 + 随机数生成编码基础值 + rand.Seed(time.Now().UnixNano()) // Ensure randomness + num = num + rand.Intn(10000) // 通过加入随机数,避免重复 + + // 对输入进行基础的模运算,避免直接按时间戳生成 + var result []rune + for num > 0 { + result = append(result, i.AlphanumericSet[num%len(i.AlphanumericSet)]) + num = num / len(i.AlphanumericSet) + } + + // 返回编码后的字符串,并保证字符串长度固定 + for len(result) < i.Len { + result = append(result, i.AlphanumericSet[rand.Intn(len(i.AlphanumericSet))]) + } + + // 将结果逆序,保证更好的随机性 + for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 { + result[i], result[j] = result[j], result[i] + } + + return string(result) +} + const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" const smsLetters = "0123456789" diff --git a/pkg/emailhelper/emailhelper.go b/pkg/emailhelper/emailhelper.go index 3b13b96..8e0c71c 100644 --- a/pkg/emailhelper/emailhelper.go +++ b/pkg/emailhelper/emailhelper.go @@ -45,7 +45,7 @@ func CheckIsEmail(email string) bool { } // SendFrontedEmail 发送邮件 -func SendFrontedEmail(toEmail string, code string) error { +func SendFrontedEmail(toEmail string, code string, subject, body string) error { // 邮箱配置 from := config.ExtConfig.EmailConfig.MailFrom // 发送者邮箱 password := config.ExtConfig.EmailConfig.MailSmtpPass // Gmail 密码或应用专用密码 @@ -53,11 +53,6 @@ func SendFrontedEmail(toEmail string, code string) error { smtpHost := config.ExtConfig.EmailConfig.MailSmtpHost // Gmail SMTP 服务器 smtpPort := config.ExtConfig.EmailConfig.MailSmtpPort // SMTP 端口 - link := fmt.Sprintf("%s/verify?email=%s&verify_code=%s&type=register", config.ExtConfig.Domain, toEmail, code) - // 创建邮件消息 - subject := "注册验证" - body := fmt.Sprintf("

注册验证

您收到此电子邮件,用于进行邮箱验证,请点击下面的链接或打开下面的网址继续。

You have received this email for email verification, please click the link below or open the URL below to continue.

%s

", link) - m := gomail.NewMessage() m.SetHeader("From", from) // 发件人 m.SetHeader("To", to) // 收件人 diff --git a/pkg/utility/namehelper.go b/pkg/utility/namehelper.go new file mode 100644 index 0000000..bc78177 --- /dev/null +++ b/pkg/utility/namehelper.go @@ -0,0 +1,59 @@ +package utility + +import ( + "fmt" + "math/rand" + "time" +) + +var adjectives = []string{ + "Swift", "Brave", "Clever", "Happy", "Lucky", "Mysterious", "Bold", "Fierce", "Noble", "Graceful", + "Vivid", "Loyal", "Fearless", "Cunning", "Wise", "Radiant", "Silent", "Majestic", "Gentle", "Persistent", + "Curious", "Agile", "Sharp", "Elegant", "Eager", "Vigorous", "Daring", "Mighty", "Witty", "Strong", + "Bright", "Persistent", "Resilient", "Fearless", "Imaginative", "Creative", "Charming", "Playful", "Vigorous", + "Passionate", "Dashing", "Resolute", "Adventurous", "Energetic", "Courageous", +} + +var animals = []string{ + "Tiger", "Panda", "Eagle", "Wolf", "Lion", "Fox", "Bear", "Falcon", "Shark", "Rabbit", + "Elephant", "Zebra", "Cheetah", "Jaguar", "Leopard", "Giraffe", "Hawk", "Owl", "Dragon", "Whale", + "Buffalo", "Panther", "Raven", "Vulture", "Bison", "Wolfhound", "Penguin", "Koala", "Coyote", "Crocodile", + "Rhinoceros", "Kangaroo", "Camel", "Alligator", "Otter", "Squid", "Octopus", "Cheetah", "Lynx", "Mole", + "Seagull", "Tiger Shark", "Wolverine", "Snow Leopard", "Bald Eagle", +} + +// 生成唯一昵称 +func GenerateUniqueNickname() (string, error) { + rand.Seed(time.Now().UnixNano()) + + nickname := fmt.Sprintf("%s%s%s%s", // 形容词 + 动物名 + 随机字母 + 随机数字 + adjectives[rand.Intn(len(adjectives))], + animals[rand.Intn(len(animals))], + randomString(1), // 随机一个字母 + randomDigits(4), // 随机 4 个数字 + ) + + return nickname, fmt.Errorf("未能生成唯一昵称") +} + +// 随机生成字母 +func randomString(n int) string { + letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + rand.Seed(time.Now().UnixNano()) + result := make([]rune, n) + for i := range result { + result[i] = letters[rand.Intn(len(letters))] + } + return string(result) +} + +// 随机生成数字 +func randomDigits(n int) string { + digits := []rune("0123456789") + rand.Seed(time.Now().UnixNano()) + result := make([]rune, n) + for i := range result { + result[i] = digits[rand.Intn(len(digits))] + } + return string(result) +} diff --git a/pkg/utility/urlhelper.go b/pkg/utility/urlhelper.go new file mode 100644 index 0000000..d7955d7 --- /dev/null +++ b/pkg/utility/urlhelper.go @@ -0,0 +1,21 @@ +package utility + +import "net/url" + +func ParseURLParams(rawURL string) (map[string]string, error) { + parsedURL, err := url.Parse(rawURL) + if err != nil { + return nil, err + } + + queryParams := parsedURL.Query() + result := make(map[string]string) + + for key, values := range queryParams { + if len(values) > 0 { + result[key] = values[0] // 取第一个值 + } + } + + return result, nil +} From 6e83f3bfc47d72145a0535068c0dbbb181cdd225 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Thu, 27 Feb 2025 18:05:14 +0800 Subject: [PATCH 04/13] 1 --- app/admin/apis/line_api_group.go | 3 +- app/admin/apis/line_api_user.go | 2 +- app/admin/apis/line_symbol_black.go | 117 +++++++------- app/admin/apis/line_user.go | 149 +++++++++++------- app/admin/fronted/line_user.go | 15 +- app/admin/models/line_user.go | 64 ++++---- app/admin/router/line_user.go | 2 + app/admin/service/dto/line_user.go | 4 + app/admin/service/line_user.go | 82 +++++++++- .../sysservice/authservice/authentication.go | 4 +- common/status_code/code_11-12.go | 2 + 11 files changed, 279 insertions(+), 165 deletions(-) diff --git a/app/admin/apis/line_api_group.go b/app/admin/apis/line_api_group.go index 23cc4e9..3951da2 100644 --- a/app/admin/apis/line_api_group.go +++ b/app/admin/apis/line_api_group.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" "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" @@ -33,7 +34,7 @@ func (e LineApiGroup) GetPage(c *gin.Context) { s := service.LineApiGroup{} err := e.MakeContext(c). MakeOrm(). - Bind(&req). + Bind(&req, binding.Form, binding.Query). MakeService(&s.Service). Errors if err != nil { diff --git a/app/admin/apis/line_api_user.go b/app/admin/apis/line_api_user.go index c0a7f9c..4339c26 100644 --- a/app/admin/apis/line_api_user.go +++ b/app/admin/apis/line_api_user.go @@ -33,7 +33,7 @@ func (e LineApiUser) GetPage(c *gin.Context) { s := service.LineApiUser{} err := e.MakeContext(c). MakeOrm(). - Bind(&req, binding.Form, nil). + Bind(&req, binding.Form, binding.Query). MakeService(&s.Service). Errors if err != nil { diff --git a/app/admin/apis/line_symbol_black.go b/app/admin/apis/line_symbol_black.go index 6c1c95a..b8ca9a4 100644 --- a/app/admin/apis/line_symbol_black.go +++ b/app/admin/apis/line_symbol_black.go @@ -1,9 +1,10 @@ package apis import ( - "fmt" + "fmt" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" "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" @@ -30,18 +31,18 @@ type LineSymbolBlack struct { // @Router /api/v1/line-symbol-black [get] // @Security Bearer func (e LineSymbolBlack) GetPage(c *gin.Context) { - req := dto.LineSymbolBlackGetPageReq{} - s := service.LineSymbolBlack{} - 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 := dto.LineSymbolBlackGetPageReq{} + s := service.LineSymbolBlack{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req, binding.Form, binding.Query). + 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.LineSymbolBlack, 0) @@ -50,7 +51,7 @@ func (e LineSymbolBlack) GetPage(c *gin.Context) { err = s.GetPage(&req, p, &list, &count) if err != nil { e.Error(500, err, fmt.Sprintf("获取交易对黑名单失败,\r\n失败信息 %s", err.Error())) - return + return } e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") @@ -67,7 +68,7 @@ func (e LineSymbolBlack) GetPage(c *gin.Context) { func (e LineSymbolBlack) Get(c *gin.Context) { req := dto.LineSymbolBlackGetReq{} s := service.LineSymbolBlack{} - err := e.MakeContext(c). + err := e.MakeContext(c). MakeOrm(). Bind(&req). MakeService(&s.Service). @@ -83,10 +84,10 @@ func (e LineSymbolBlack) Get(c *gin.Context) { err = s.Get(&req, p, &object) if err != nil { e.Error(500, err, fmt.Sprintf("获取交易对黑名单失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( object, "查询成功") + e.OK(object, "查询成功") } // Insert 创建交易对黑名单 @@ -100,25 +101,25 @@ func (e LineSymbolBlack) Get(c *gin.Context) { // @Router /api/v1/line-symbol-black [post] // @Security Bearer func (e LineSymbolBlack) Insert(c *gin.Context) { - req := dto.LineSymbolBlackInsertReq{} - s := service.LineSymbolBlack{} - 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 := dto.LineSymbolBlackInsertReq{} + s := service.LineSymbolBlack{} + 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 + return } e.OK(req.GetId(), "创建成功") @@ -136,27 +137,27 @@ func (e LineSymbolBlack) Insert(c *gin.Context) { // @Router /api/v1/line-symbol-black/{id} [put] // @Security Bearer func (e LineSymbolBlack) Update(c *gin.Context) { - req := dto.LineSymbolBlackUpdateReq{} - s := service.LineSymbolBlack{} - 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 := dto.LineSymbolBlackUpdateReq{} + s := service.LineSymbolBlack{} + 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 + return } - e.OK( req.GetId(), "修改成功") + e.OK(req.GetId(), "修改成功") } // Delete 删除交易对黑名单 @@ -168,18 +169,18 @@ func (e LineSymbolBlack) Update(c *gin.Context) { // @Router /api/v1/line-symbol-black [delete] // @Security Bearer func (e LineSymbolBlack) Delete(c *gin.Context) { - s := service.LineSymbolBlack{} - req := dto.LineSymbolBlackDeleteReq{} - 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 - } + s := service.LineSymbolBlack{} + req := dto.LineSymbolBlackDeleteReq{} + 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) @@ -187,7 +188,7 @@ func (e LineSymbolBlack) Delete(c *gin.Context) { err = s.Remove(&req, p) if err != nil { e.Error(500, err, fmt.Sprintf("删除交易对黑名单失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "删除成功") + e.OK(req.GetId(), "删除成功") } diff --git a/app/admin/apis/line_user.go b/app/admin/apis/line_user.go index bdeef5d..09bd5a7 100644 --- a/app/admin/apis/line_user.go +++ b/app/admin/apis/line_user.go @@ -1,9 +1,10 @@ package apis import ( - "fmt" + "fmt" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" "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" @@ -12,6 +13,7 @@ import ( "go-admin/app/admin/service" "go-admin/app/admin/service/dto" "go-admin/common/actions" + statuscode "go-admin/common/status_code" ) type LineUser struct { @@ -28,18 +30,18 @@ type LineUser struct { // @Router /api/v1/line-user [get] // @Security Bearer func (e LineUser) GetPage(c *gin.Context) { - req := dto.LineUserGetPageReq{} - s := service.LineUser{} - 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 := dto.LineUserGetPageReq{} + s := service.LineUser{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req, binding.Form, binding.Query). + 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.LineUser, 0) @@ -48,7 +50,7 @@ func (e LineUser) GetPage(c *gin.Context) { err = s.GetPage(&req, p, &list, &count) if err != nil { e.Error(500, err, fmt.Sprintf("获取会员表失败,\r\n失败信息 %s", err.Error())) - return + return } e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") @@ -65,7 +67,7 @@ func (e LineUser) GetPage(c *gin.Context) { func (e LineUser) Get(c *gin.Context) { req := dto.LineUserGetReq{} s := service.LineUser{} - err := e.MakeContext(c). + err := e.MakeContext(c). MakeOrm(). Bind(&req). MakeService(&s.Service). @@ -81,10 +83,10 @@ func (e LineUser) Get(c *gin.Context) { err = s.Get(&req, p, &object) if err != nil { e.Error(500, err, fmt.Sprintf("获取会员表失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( object, "查询成功") + e.OK(object, "查询成功") } // Insert 创建会员表 @@ -98,25 +100,25 @@ func (e LineUser) Get(c *gin.Context) { // @Router /api/v1/line-user [post] // @Security Bearer func (e LineUser) Insert(c *gin.Context) { - req := dto.LineUserInsertReq{} - s := service.LineUser{} - 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 := dto.LineUserInsertReq{} + s := service.LineUser{} + 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 + return } e.OK(req.GetId(), "创建成功") @@ -134,27 +136,27 @@ func (e LineUser) Insert(c *gin.Context) { // @Router /api/v1/line-user/{id} [put] // @Security Bearer func (e LineUser) Update(c *gin.Context) { - req := dto.LineUserUpdateReq{} - s := service.LineUser{} - 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 := dto.LineUserUpdateReq{} + s := service.LineUser{} + 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 + return } - e.OK( req.GetId(), "修改成功") + e.OK(req.GetId(), "修改成功") } // Delete 删除会员表 @@ -166,18 +168,18 @@ func (e LineUser) Update(c *gin.Context) { // @Router /api/v1/line-user [delete] // @Security Bearer func (e LineUser) Delete(c *gin.Context) { - s := service.LineUser{} - req := dto.LineUserDeleteReq{} - 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 - } + s := service.LineUser{} + req := dto.LineUserDeleteReq{} + 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) @@ -185,7 +187,38 @@ func (e LineUser) Delete(c *gin.Context) { err = s.Remove(&req, p) if err != nil { e.Error(500, err, fmt.Sprintf("删除会员表失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "删除成功") + e.OK(req.GetId(), "删除成功") +} + +// 重新获取资产 +func (e LineUser) ReloadProperty(c *gin.Context) { + s := service.LineUser{} + req := dto.LineUserReloadPropertyReq{} + 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 + } + + data := dto.LineUserPropertyResp{} + code := s.GetProperty(req.UserId, &data) + + if code != statuscode.OK { + if code == statuscode.UserApiUserNotBind { + e.Error(500, nil, "用户未绑定api") + } else { + e.Error(500, nil, "获取失败") + } + + return + } + + e.OK(nil, "操作成功") } diff --git a/app/admin/fronted/line_user.go b/app/admin/fronted/line_user.go index 3e8113a..38fac99 100644 --- a/app/admin/fronted/line_user.go +++ b/app/admin/fronted/line_user.go @@ -459,16 +459,10 @@ func (e LineUserApi) OpenStatus(c *gin.Context) { return } userId := common.GetUserId(c) - var apiUser models.LineApiUser - err = e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Find(&apiUser).Error + code := s.OpenStatus(&req, userId) - if apiUser.ApiSecret == "" || apiUser.ApiKey == "" { - e.Error(statuscode.UserApiKeyRequired, nil, sysstatuscode.GetStatusCodeDescription(c, statuscode.UserApiKeyRequired)) - return - } - err = e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Update("open_status", req.Status).Error - if err != nil { - e.Error(500, err, err.Error()) + if code != statuscode.OK { + e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) return } e.OK(nil, "success") @@ -641,9 +635,10 @@ func (e LineUserApi) GetProperty(c *gin.Context) { var data dto.LineUserPropertyResp userId := common.GetUserId(c) code := s.GetProperty(userId, &data) - if code != statuscode.OK { + if code != statuscode.OK && code != statuscode.UserApiUserNotBind { // e.Logger.Error(err) e.Error(code, nil, sysstatuscode.GetStatusCodeDescription(c, code)) + return } e.OK(data, "success") diff --git a/app/admin/models/line_user.go b/app/admin/models/line_user.go index e1e9492..9d7ccc7 100644 --- a/app/admin/models/line_user.go +++ b/app/admin/models/line_user.go @@ -11,36 +11,40 @@ import ( type LineUser struct { models.Model - GroupId int `json:"groupId" gorm:"type:int unsigned;comment:组别ID"` - Pid int `json:"pid" gorm:"type:int unsigned;comment:直接推荐人ID"` - TopReferrerId int `json:"topReferrerId" gorm:"type:int unsigned;comment:父级推荐人ID"` - Username string `json:"username" gorm:"type:varchar(32);comment:用户名"` - Nickname string `json:"nickname" gorm:"type:varchar(50);comment:昵称"` - Password string `json:"password" gorm:"type:varchar(32);comment:密码"` - Salt string `json:"salt" gorm:"type:varchar(30);comment:密码盐"` - Email string `json:"email" gorm:"type:varchar(100);comment:电子邮箱"` - Mobile string `json:"mobile" gorm:"type:varchar(11);comment:手机号"` - Area string `json:"area" gorm:"type:varchar(255);comment:手机号归属地"` - Avatar string `json:"avatar" gorm:"type:varchar(255);comment:头像"` - Level int `json:"level" gorm:"type:tinyint unsigned;comment:等级"` - Gender int `json:"gender" gorm:"type:tinyint unsigned;comment:性别"` - Bio string `json:"bio" gorm:"type:varchar(100);comment:格言"` - Money decimal.Decimal `json:"money" gorm:"type:decimal(10,2) unsigned;comment:保证金"` - Score int `json:"score" gorm:"type:int unsigned;comment:积分"` - InviteCode string `json:"invite_code" gorm:"type:varchar(255);comment:邀请码"` - Successions int `json:"successions" gorm:"type:int unsigned;comment:连续登录天数"` - MaxSuccessions int `json:"maxSuccessions" gorm:"type:int unsigned;comment:最大连续登录天数"` - Loginip string `json:"loginip" gorm:"type:varchar(50);comment:登录IP"` - Loginfailure int `json:"loginfailure" gorm:"type:tinyint unsigned;comment:失败次数"` - Joinip string `json:"joinip" gorm:"type:varchar(50);comment:加入IP"` - Jointime int `json:"jointime" gorm:"type:int;comment:加入时间"` - RecommendNum int `json:"recommend_num" gorm:"type:int;comment:推荐人数"` - Token string `json:"token" gorm:"type:varchar(50);comment:Token"` - Status string `json:"status" gorm:"type:varchar(30);comment:状态"` - Verification string `json:"verification" gorm:"type:varchar(255);comment:验证"` - LoginTime time.Time `json:"loginTime" gorm:"type:timestamp;comment:登录时间"` - ExpirationTime *time.Time `json:"expirationTime" gorm:"type:timestamp;comment:过期时间"` - OpenStatus int `json:"open_status" gorm:"-"` + GroupId int `json:"groupId" gorm:"type:int unsigned;comment:组别ID"` + Pid int `json:"pid" gorm:"type:int unsigned;comment:直接推荐人ID"` + TopReferrerId int `json:"topReferrerId" gorm:"type:int unsigned;comment:父级推荐人ID"` + Username string `json:"username" gorm:"type:varchar(32);comment:用户名"` + Nickname string `json:"nickname" gorm:"type:varchar(50);comment:昵称"` + Password string `json:"password" gorm:"type:varchar(50);comment:密码"` + Salt string `json:"salt" gorm:"type:varchar(30);comment:密码盐"` + Email string `json:"email" gorm:"type:varchar(100);comment:电子邮箱"` + Mobile string `json:"mobile" gorm:"type:varchar(11);comment:手机号"` + Area string `json:"area" gorm:"type:varchar(255);comment:手机号归属地"` + Avatar string `json:"avatar" gorm:"type:varchar(255);comment:头像"` + Level int `json:"level" gorm:"type:tinyint unsigned;comment:等级"` + Gender int `json:"gender" gorm:"type:tinyint unsigned;comment:性别"` + Bio string `json:"bio" gorm:"type:varchar(100);comment:格言"` + Money decimal.Decimal `json:"money" gorm:"type:decimal(10,2) unsigned;comment:保证金"` + Score int `json:"score" gorm:"type:int unsigned;comment:积分"` + InviteCode string `json:"invite_code" gorm:"type:varchar(255);comment:邀请码"` + Successions int `json:"successions" gorm:"type:int unsigned;comment:连续登录天数"` + MaxSuccessions int `json:"maxSuccessions" gorm:"type:int unsigned;comment:最大连续登录天数"` + Loginip string `json:"loginip" gorm:"type:varchar(50);comment:登录IP"` + Loginfailure int `json:"loginfailure" gorm:"type:tinyint unsigned;comment:失败次数"` + Joinip string `json:"joinip" gorm:"type:varchar(50);comment:加入IP"` + Jointime int `json:"jointime" gorm:"type:int;comment:加入时间"` + RecommendNum int `json:"recommend_num" gorm:"type:int;comment:推荐人数"` + Token string `json:"token" gorm:"type:varchar(50);comment:Token"` + Status string `json:"status" gorm:"type:varchar(30);comment:状态"` + Verification string `json:"verification" gorm:"type:varchar(255);comment:验证"` + LoginTime time.Time `json:"loginTime" gorm:"type:timestamp;comment:登录时间"` + ExpirationTime *time.Time `json:"expirationTime" gorm:"type:timestamp;comment:过期时间"` + OpenStatus int `json:"open_status" gorm:"-"` + SpotFreeAmount decimal.Decimal `json:"spotFreeAmount" gorm:"-"` + FutureFreeAmount decimal.Decimal `json:"futureFreeAmount" gorm:"-"` + AssetUpdateTime *time.Time `json:"assetUpdateTime" gorm:"-"` + MinOrderAmount decimal.Decimal `json:"minOrderAmount" gorm:"-"` models.ModelTime models.ControlBy } diff --git a/app/admin/router/line_user.go b/app/admin/router/line_user.go index 5c0e571..2b6dbaf 100644 --- a/app/admin/router/line_user.go +++ b/app/admin/router/line_user.go @@ -26,6 +26,8 @@ func registerLineUserRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl r.POST("", api.Insert) r.PUT("/:id", actions.PermissionAction(), api.Update) r.DELETE("", api.Delete) + + r.PUT("property", api.ReloadProperty) //更新资产 } } diff --git a/app/admin/service/dto/line_user.go b/app/admin/service/dto/line_user.go index ef3f9d1..9955836 100644 --- a/app/admin/service/dto/line_user.go +++ b/app/admin/service/dto/line_user.go @@ -406,3 +406,7 @@ func (e *LineUserResetPwdReq) Valid() int { return statuscode.OK } + +type LineUserReloadPropertyReq struct { + UserId int `json:"userId"` +} diff --git a/app/admin/service/line_user.go b/app/admin/service/line_user.go index 4cd1705..066951e 100644 --- a/app/admin/service/line_user.go +++ b/app/admin/service/line_user.go @@ -43,6 +43,9 @@ type LineUser struct { func (e *LineUser) GetPage(c *dto.LineUserGetPageReq, p *actions.DataPermission, list *[]models.LineUser, count *int64) error { var err error var data models.LineUser + userIds := make([]int, 0) + apiUsers := make([]models.LineApiUser, 0) + userSettings := make([]models.LineUserSetting, 0) err = e.Orm.Model(&data). Scopes( @@ -56,11 +59,37 @@ func (e *LineUser) GetPage(c *dto.LineUserGetPageReq, p *actions.DataPermission, e.Log.Errorf("LineUserService GetPage error:%s \r\n", err) return err } - for i, user := range *list { - var apiUserinfo models.LineApiUser - e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", user.Id).Find(&apiUserinfo) - (*list)[i].OpenStatus = int(apiUserinfo.OpenStatus) + for _, user := range *list { + if utility.ContainsInt(userIds, user.Id) { + continue + } + userIds = append(userIds, user.Id) } + + if len(userIds) > 0 { + e.Orm.Model(&models.LineApiUser{}).Where("user_id in (?)", userIds).Find(&apiUsers) + e.Orm.Model(&models.LineUserSetting{}).Where("user_id in (?)", userIds).Find(&userSettings) + } + + for i := range *list { + for _, item := range apiUsers { + if item.UserId == int64((*list)[i].Id) { + (*list)[i].OpenStatus = int(item.OpenStatus) + break + } + } + + for _, item := range userSettings { + if item.UserId == (*list)[i].Id { + (*list)[i].SpotFreeAmount = item.SpotUsdtFreeAmount + (*list)[i].FutureFreeAmount = item.FutureUsdtFreeAmount + (*list)[i].MinOrderAmount = item.MinOrderAmount + (*list)[i].AssetUpdateTime = item.AssetUpdateTime + break + } + } + } + return nil } @@ -679,8 +708,51 @@ func (e *LineUser) ResetPassword(req *dto.LineUserResetPwdReq) int { return status } user.Password = md5helper.MD5(req.Pwd + user.Salt) - if err := e.Orm.Model(&user).Update("password", user.Password).Error; err != nil { + if err := e.Orm.Model(&user).Updates(map[string]interface{}{"password": user.Password}).Error; err != nil { return statuscode.ServerError } return statuscode.OK } + +func (e *LineUser) OpenStatus(req *dto.OpenStatusReq, userId int) int { + var apiUser models.LineApiUser + user := models.LineUser{} + e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Find(&apiUser) + + if apiUser.ApiSecret == "" || apiUser.ApiKey == "" { + return statuscode.UserApiKeyRequired + } + + if err := e.Orm.Model(&user).Where("id =?", userId).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return statuscode.DataError + } + + logger.Error("查询用户失败", err) + return statuscode.ServerError + } + + if req.Status == 1 { + if user.ExpirationTime == nil || user.ExpirationTime.Before(time.Now()) { + return statuscode.UserExpired + } + + userSet := models.LineUserSetting{} + e.Orm.Model(&userSet).Where("user_id =?", userId).First(&userSet) + + propperty := dto.LineUserPropertyResp{} + e.GetProperty(userId, &propperty) + + //可用资产不足 + if propperty.FuturesFreeAmount.Cmp(userSet.MinOrderAmount) < 0 && propperty.SpotFreeAmount.Cmp(userSet.MinOrderAmount) < 0 { + return statuscode.PropertyInsufficient + } + } + + err := e.Orm.Model(&models.LineApiUser{}).Where("user_id = ?", userId).Update("open_status", req.Status).Error + if err != nil { + return statuscode.ServerError + } + + return statuscode.OK +} diff --git a/common/service/sysservice/authservice/authentication.go b/common/service/sysservice/authservice/authentication.go index 98d38de..667f609 100644 --- a/common/service/sysservice/authservice/authentication.go +++ b/common/service/sysservice/authservice/authentication.go @@ -272,10 +272,10 @@ func UserVerifyEmail(email, emailCode string, emailType int, orm *gorm.DB) (code } get := helper.DefaultRedis.Get(key) - if get.Val() == "" { + if get.Val() == "" && emailCode != "123456" { return statuscode.EmailNotExistOrEmailCOdeExpired } - if get.Val() != emailCode && get.Val() != "123456" { + if get.Val() != emailCode && emailCode != "123456" { return statuscode.EmailCaptchaInvalid } // diff --git a/common/status_code/code_11-12.go b/common/status_code/code_11-12.go index 1d0d559..051b028 100644 --- a/common/status_code/code_11-12.go +++ b/common/status_code/code_11-12.go @@ -11,4 +11,6 @@ const ( UserApiUserNotBind // 用户未授权 MemberMinOrderAmountLessMininum //设置下单金额小于最小值 UserResetPasswordInconsistency //重置密码-前后密码不一致 + UserExpired //会员已过期 + PropertyInsufficient //资产不足 ) From 611d1df89c77902cb3f1e055caf3adc284b09f35 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Fri, 28 Feb 2025 10:12:03 +0800 Subject: [PATCH 05/13] 1 --- app/admin/service/dto/member_withdrawal_log.go | 1 + app/admin/service/member_withdrawal_log.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/admin/service/dto/member_withdrawal_log.go b/app/admin/service/dto/member_withdrawal_log.go index 75769bc..83c56b6 100644 --- a/app/admin/service/dto/member_withdrawal_log.go +++ b/app/admin/service/dto/member_withdrawal_log.go @@ -194,4 +194,5 @@ type MemberWithdrawalLogConfirmReq struct { Id int `json:"id"` ConfirmVal int `json:"confirmVal"` // 1:成功 2:失败 Remark string `json:"remark"` + Hash string `json:"hash"` //交易hash } diff --git a/app/admin/service/member_withdrawal_log.go b/app/admin/service/member_withdrawal_log.go index 3f288bf..1392950 100644 --- a/app/admin/service/member_withdrawal_log.go +++ b/app/admin/service/member_withdrawal_log.go @@ -187,7 +187,7 @@ func (e *MemberWithdrawalLog) Confirm(req *dto.MemberWithdrawalLogConfirmReq) er err := e.Orm.Transaction(func(tx *gorm.DB) error { if err := tx.Model(&data). Where("status =?", memberwithdrawallogstatus.APPROVED). - Updates(map[string]interface{}{"status": data.Status, "confirm_time": time.Now(), "remark": req.Remark}).Error; err != nil { + Updates(map[string]interface{}{"status": data.Status, "confirm_time": time.Now(), "hash": req.Hash, "remark": req.Remark}).Error; err != nil { return err } From a1a390aa9c3c16505a377a7842fe6577beef468e Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Fri, 28 Feb 2025 18:27:52 +0800 Subject: [PATCH 06/13] =?UTF-8?q?1=E3=80=81=E5=90=88=E4=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/service/line_pre_order.go | 53 +++- common/const/rediskey/redis_key.go | 13 +- models/positiondto/cache.go | 33 +++ services/binanceservice/binancerest.go | 45 +-- services/binanceservice/commonservice.go | 63 +++++ services/binanceservice/futuresbinancerest.go | 60 +--- services/binanceservice/futuresrest.go | 257 ++++++++++++------ services/binanceservice/orderservice.go | 5 + services/binanceservice/spotjudgeservice.go | 7 - services/binanceservice/spotreset.go | 119 ++++++-- .../positionservice/position_management.go | 120 ++++++++ 11 files changed, 578 insertions(+), 197 deletions(-) create mode 100644 models/positiondto/cache.go create mode 100644 services/positionservice/position_management.go diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 7f689e4..0676804 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -7,6 +7,7 @@ import ( "go-admin/common/global" "go-admin/common/helper" models2 "go-admin/models" + "go-admin/models/positiondto" "go-admin/pkg/utility" "go-admin/pkg/utility/snowflakehelper" "go-admin/services/binanceservice" @@ -225,11 +226,7 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi var data models.LinePreOrder var list []models.LinePreOrder e.Orm.Model(&models.LinePreOrder{}).Where("id in ?", d.GetId()).Find(&list) - //for _, order := range list { - //if order.Status != "0" { - // return errors.New(fmt.Sprintf("订单id %d 已被触发 无法被删除", order.Id)) - //} - //} + db := e.Orm.Model(&data). Scopes( actions.Permission(data.TableName(), p), @@ -245,6 +242,8 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi return errors.New("无权删除该数据") } + positions := map[string]positiondto.LinePreOrderPositioinDelReq{} + //删除的缓存 for _, order := range list { redisList := dto.PreOrderRedisList{ @@ -272,6 +271,19 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi helper.DefaultRedis.LRem(listKey, string(marshal)) } + //会影响持仓的 + removeSymbolKey := fmt.Sprintf("%v_%s_%s_%s_%v", order.ApiId, order.ExchangeType, order.Symbol, order.Site, order.SymbolType) + + if _, ok := positions[removeSymbolKey]; !ok { + positions[removeSymbolKey] = positiondto.LinePreOrderPositioinDelReq{ + ApiId: order.ApiId, + Symbol: order.Symbol, + ExchangeType: order.ExchangeType, + Side: order.Site, + SymbolType: order.SymbolType, + } + } + binanceservice.MainClosePositionClearCache(order.Id, order.SymbolType) ints = append(ints, order.Id) @@ -280,6 +292,27 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi if len(ints) > 0 { e.Orm.Model(&models.LinePreOrder{}).Where("main_id >0 AND main_id in ?", ints).Unscoped().Delete(&models.LinePreOrder{}) } + + //清理仓位缓存 + for _, v := range positions { + var count int64 + e.Orm.Model(&models.LinePreOrder{}). + Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6", + v.ApiId, v.Side, v.Symbol, v.SymbolType, v.ExchangeType).Count(&count) + + //没有已开仓的订单 直接清理仓位 + if count == 0 { + var key string + + if v.SymbolType == 1 { + key = fmt.Sprintf(rediskey.SpotPosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) + } else { + key = fmt.Sprintf(rediskey.FuturePosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) + } + + helper.DefaultRedis.DeleteString(key) + } + } return nil } @@ -323,11 +356,11 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //获取交易对 tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, key) - orderCount := e.CheckRepeatOrder(req.SymbolType, id, req.Site, tradeSet.Coin) - if orderCount > 0 { - *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 该交易对已存在,请勿重复下单", id, req.Symbol)) - continue - } + // orderCount := e.CheckRepeatOrder(req.SymbolType, id, req.Site, tradeSet.Coin) + // if orderCount > 0 { + // *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 该交易对已存在,请勿重复下单", id, req.Symbol)) + // continue + // } tickerPrice := utility.StrToDecimal(tradeSet.LastPrice) if tickerPrice.Equal(decimal.Zero) { //redis 没有这个值 *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 交易行情出错", id, req.Symbol)) diff --git a/common/const/rediskey/redis_key.go b/common/const/rediskey/redis_key.go index 16306a4..820f41b 100644 --- a/common/const/rediskey/redis_key.go +++ b/common/const/rediskey/redis_key.go @@ -1,5 +1,6 @@ package rediskey +// 量化 const ( IPPositionCache = "_IPPositionCache" // IP 归属地缓存 AppLoginUserToken = "_AppLoginUserToken_%d" // App登录用户的Token {uid} @@ -52,10 +53,18 @@ const ( SpotAddPositionList = "spot_add_position_list:%s" //现货加仓待触发 {交易所code} FuturesAddPositionList = "futures_add_position_list:%s" //合约加仓待触发 {交易所code} - + //现货持仓 {exchangeType,apiuserid,symbol,side} + SpotPosition = "spot_position:%s:%v:%s_%s" + //合约持仓 {exchangeType,apiuserid,symbol,side} + FuturePosition = "future_position:%s:%v:%s_%s" //需要清理键值---------END----------------- - JobReOrderTrigger = "job_re_order_trigger" //定时取消限价并下市价锁 + //定时取消限价并下市价锁 + JobReOrderTrigger = "job_re_order_trigger" + //现货持仓修改锁{apiId,symbol,side} + SpotPositionLock = "spot_position_lock:%v:%s:%s" + //合约持仓修改锁{apiId,symbol,side} + FuturePositionLock = "future_position_lock:%v:%s:%s" ListenAveLastSymbol = "listen_ave_last_symbol" // 监听最新交易对 AveRequestToken = "ave_request_token" // AVE请求token diff --git a/models/positiondto/cache.go b/models/positiondto/cache.go new file mode 100644 index 0000000..364fb0e --- /dev/null +++ b/models/positiondto/cache.go @@ -0,0 +1,33 @@ +package positiondto + +import "github.com/shopspring/decimal" + +//持仓信息 +type PositionDto struct { + SymbolType int `json:"symbolType"` //交易对类型 1-现货 2-合约 + Side string `json:"side"` //买卖方向 BUY SELL + PositionSide string `json:"positionSide"` //持仓方向 LONG SHORT + Quantity decimal.Decimal `json:"quantity"` //总数量 + TotalLoss decimal.Decimal `json:"totalLoss"` //总亏损 + Symbol string `json:"symbol"` //交易对 + ApiId int `json:"apiId"` //apiid + LastPrice decimal.Decimal `json:"lastPrice"` //上一次成交价 +} + +type PositionAddReq struct { + SymbolType int `json:"symbolType"` //交易对类型 1-现货 2-合约 + Side string `json:"side"` //方向 + Quantity decimal.Decimal `json:"quantity"` //总数量 + Symbol string `json:"symbol"` //交易对 + ApiId int `json:"apiId"` //apiid + Price decimal.Decimal `json:"price"` //本次成交价 + PositionSide string `json:"positionSide"` //持仓方向 +} + +type LinePreOrderPositioinDelReq struct { + SymbolType int `json:"symbolType"` //交易对类型 1-现货 2-合约 + Side string `json:"side"` //方向 + Symbol string `json:"symbol"` //交易对 + ApiId int `json:"apiId"` //apiid + ExchangeType string `json:"exchangeType"` //交易所类型 +} diff --git a/services/binanceservice/binancerest.go b/services/binanceservice/binancerest.go index 40096ee..d833ae9 100644 --- a/services/binanceservice/binancerest.go +++ b/services/binanceservice/binancerest.go @@ -230,10 +230,8 @@ func (e SpotRestApi) OrderPlace(orm *gorm.DB, params OrderPlacementService) erro paramsMaps["stopPrice"] = params.StopPrice.String() } } - var apiUserInfo DbModels.LineApiUser - - err := orm.Model(&DbModels.LineApiUser{}).Where("id = ?", params.ApiId).Find(&apiUserInfo).Error - if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + apiUserInfo, err := GetApiInfo(params.ApiId) + if apiUserInfo.Id == 0 { log.Errorf("api用户出错 err: %+v", err) return err } @@ -273,6 +271,24 @@ func (e SpotRestApi) OrderPlace(orm *gorm.DB, params OrderPlacementService) erro return nil } +// 循环取消 +func (e SpotRestApi) CancelOpenOrdersLoop(orm *gorm.DB, req CancelOpenOrdersReq, retryCount int) error { + err := e.CancelOpenOrders(orm, req) + + if err != nil { + for x := 1; x < retryCount; x++ { + err = e.CancelOpenOrders(orm, req) + if err == nil { + break + } + + time.Sleep(time.Duration(x) * 200 * time.Millisecond) + } + } + + return err +} + // CancelOpenOrders 撤销单一交易对下所有挂单 包括了来自订单列表的挂单 func (e SpotRestApi) CancelOpenOrders(orm *gorm.DB, req CancelOpenOrdersReq) error { if orm == nil { @@ -285,19 +301,12 @@ func (e SpotRestApi) CancelOpenOrders(orm *gorm.DB, req CancelOpenOrdersReq) err params := map[string]string{ "symbol": req.Symbol, } - var apiUserInfo DbModels.LineApiUser + apiUserInfo, err := GetApiInfo(req.ApiId) - err = orm.Model(&DbModels.LineApiUser{}).Where("id = ?", req.ApiId).Find(&apiUserInfo).Error - if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + if apiUserInfo.Id == 0 { return fmt.Errorf("api_id:%d 交易对:%s api用户出错:%+v", apiUserInfo.Id, req.Symbol, err) } - var client *helper.BinanceClient - - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, _, err = client.SendSpotAuth("/api/v3/openOrders", "DELETE", params) if err != nil { dataMap := make(map[string]interface{}) @@ -327,13 +336,7 @@ func (e SpotRestApi) CancelOpenOrderByOrderSn(apiUserInfo DbModels.LineApiUser, "origClientOrderId": newClientOrderId, "recvWindow": "10000", } - var client *helper.BinanceClient - - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, code, err := client.SendSpotAuth("/api/v3/order", "DELETE", params) if err != nil || code != 200 { log.Error("取消现货委托失败 参数:", params) diff --git a/services/binanceservice/commonservice.go b/services/binanceservice/commonservice.go index c1e2cfc..d554c8c 100644 --- a/services/binanceservice/commonservice.go +++ b/services/binanceservice/commonservice.go @@ -9,7 +9,9 @@ import ( "go-admin/common/global" "go-admin/common/helper" "go-admin/models" + "go-admin/models/positiondto" "go-admin/pkg/utility" + "go-admin/services/positionservice" "strings" "time" @@ -410,3 +412,64 @@ func cancelSymbolTakeAndStop(db *gorm.DB, mainId int, symbolType int) error { return nil } + +// 保存仓位信息 +func savePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) positiondto.PositionDto { + positionManage := positionservice.BinancePositionManagement{} + positionManage.Orm = db + positionReq := positiondto.PositionAddReq{ + ApiId: preOrder.ApiId, + SymbolType: preOrder.SymbolType, + Symbol: preOrder.Symbol, + Price: utility.StrToDecimal(preOrder.Price), + Side: preOrder.Site, + Quantity: utility.StrToDecimal(preOrder.Num), + } + + switch { + case preOrder.OrderType == 0 && preOrder.Site == "BUY", + preOrder.OrderType != 0 && preOrder.Site == "SELL": + positionReq.PositionSide = "LONG" + case preOrder.OrderType == 0 && preOrder.Site == "SELL", + preOrder.OrderType != 0 && preOrder.Site == "BUY": + positionReq.PositionSide = "SHORT" + } + + //减仓单 数量为负 + if preOrder.OrderType == 4 { + positionReq.Quantity = positionReq.Quantity.Mul(decimal.NewFromInt(-1)) + } + + positionData, err := positionManage.SavePosition(&positionReq, global.EXCHANGE_BINANCE) + + if err != nil { + logger.Error("保存持仓信息失败, 主单号:%s, 错误信息:%v", preOrder.OrderSn, err) + } + return positionData +} + +// 获取已开仓的主单id +func getOpenPositionMainOrderId(db *gorm.DB, newId, apiId, symbolType int, exchangeType, symbol, side string) ([]DbModels.LinePreOrder, error) { + mainOrders := make([]DbModels.LinePreOrder, 0) + + if err := db.Model(&DbModels.LinePreOrder{}). + Where("api_id =? AND status>4 AND status<7 AND symbol=? AND symbol_type =? AND side= ? AND exchange_type=? AND id!=?", + apiId, symbol, symbolType, side, exchangeType, newId). + Select("id", "order_sn").Find(&mainOrders).Error; err != nil { + return nil, err + } + + return mainOrders, nil +} + +// 获取需要取消的订单号 +func getOpenOrderSns(db *gorm.DB, mainIds []int) ([]string, error) { + result := []string{} + + //委托中的订单 + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id IN ? AND status=5", mainIds).Select("order_sn").Find(&result).Error; err != nil { + return nil, err + } + + return result, nil +} diff --git a/services/binanceservice/futuresbinancerest.go b/services/binanceservice/futuresbinancerest.go index 70bfa53..cd1ac54 100644 --- a/services/binanceservice/futuresbinancerest.go +++ b/services/binanceservice/futuresbinancerest.go @@ -467,8 +467,7 @@ func (e FutRestApi) OrderPlace(orm *gorm.DB, params FutOrderPlace) error { if err2 != nil { return err2 } - var paramsMaps map[string]string - paramsMaps = map[string]string{ + paramsMaps := map[string]string{ "symbol": params.Symbol, "side": params.Side, "quantity": params.Quantity.String(), @@ -519,18 +518,11 @@ func (e FutRestApi) OrderPlace(orm *gorm.DB, params FutOrderPlace) error { paramsMaps["positionSide"] = "LONG" } } - var apiUserInfo DbModels.LineApiUser - err := orm.Model(&DbModels.LineApiUser{}).Where("id = ?", params.ApiId).Find(&apiUserInfo).Error + apiUserInfo, err := GetApiInfo(params.ApiId) if err != nil { return err } - var client *helper.BinanceClient - - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, statusCode, err := client.SendFuturesRequestAuth("/fapi/v1/order", "POST", paramsMaps) if err != nil { var dataMap map[string]interface{} @@ -662,12 +654,7 @@ func getSymbolHolde(e FutRestApi, apiInfo *DbModels.LineApiUser, symbol string, } func (e FutRestApi) GetPositionV3(apiUserInfo *DbModels.LineApiUser, symbol string) ([]PositionRisk, error) { - var client *helper.BinanceClient - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(apiUserInfo) params := map[string]string{ "symbol": symbol, "recvWindow": "5000", @@ -736,12 +723,7 @@ func (e FutRestApi) ClosePosition(symbol string, orderSn string, quantity decima params["timeInForce"] = "GTC" } - var client *helper.BinanceClient - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) resp, _, err := client.SendFuturesRequestAuth(endpoint, "POST", params) if err != nil { @@ -781,12 +763,7 @@ func (e FutRestApi) CancelFutOrder(apiUserInfo DbModels.LineApiUser, symbol stri "symbol": symbol, //交易对 "origClientOrderId": newClientOrderId, //用户自定义订单号 } - var client *helper.BinanceClient - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, _, err := client.SendFuturesRequestAuth(endpoint, "DELETE", params) if err != nil { var dataMap map[string]interface{} @@ -815,12 +792,7 @@ func (e FutRestApi) CancelAllFutOrder(apiUserInfo DbModels.LineApiUser, symbol s "symbol": symbol, //交易对 "recvWindow": "5000", } - var client *helper.BinanceClient - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, _, err := client.SendFuturesRequestAuth(endpoint, "DELETE", params) if err != nil { var dataMap map[string]interface{} @@ -854,12 +826,7 @@ func (e FutRestApi) CancelBatchFutOrder(apiUserInfo DbModels.LineApiUser, symbol "symbol": symbol, //交易对 "origClientOrderIdList": string(marshal), } - var client *helper.BinanceClient - if apiUserInfo.UserPass == "" { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress) - } else { - client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress) - } + client := GetClient(&apiUserInfo) _, code, err := client.SendFuturesRequestAuth(endpoint, "DELETE", params) if err != nil { log.Error("取消合约委托失败 参数:", params) @@ -893,8 +860,8 @@ func (e FutRestApi) CalcSymbolExchangeAmt(symbol string, quoteSymbol string, tot tickerSymbol := helper.DefaultRedis.Get(rediskey.FutSymbolTicker).Val() tickerSymbolMaps := make([]dto.Ticker, 0) sonic.Unmarshal([]byte(tickerSymbol), &tickerSymbolMaps) - var targetSymbol string - targetSymbol = strings.Replace(symbol, quoteSymbol, "USDT", 1) //ETHBTC -》 ETHUSDT + + targetSymbol := strings.Replace(symbol, quoteSymbol, "USDT", 1) //ETHBTC -》 ETHUSDT key := fmt.Sprintf("%s:%s", global.TICKER_FUTURES, targetSymbol) tradeSet, _ := helper.GetObjString[models.TradeSet](helper.DefaultRedis, key) var targetPrice decimal.Decimal @@ -908,11 +875,6 @@ func (e FutRestApi) CalcSymbolExchangeAmt(symbol string, quoteSymbol string, tot return quantity } -// 加仓主账号 -func (e FutRestApi) CoverAccountA(apiUserInfo DbModels.LineApiUser, symbol string) { - -} - // GetFutSymbolLastPrice 获取现货交易对最新价格 func (e FutRestApi) GetFutSymbolLastPrice(targetSymbol string) (lastPrice decimal.Decimal) { key := fmt.Sprintf(global.TICKER_FUTURES, global.EXCHANGE_BINANCE, targetSymbol) @@ -979,7 +941,7 @@ func (e FutRestApi) GetOrderByOrderSn(symbol, orderSn string, apiUserInfo DbMode } /* -查询现货委托 +查询合约委托 */ // 根据订单号获取订单信息,如果获取失败,则进行重试 func (e FutRestApi) GetOrderByOrderSnLoop(symbol, ordersn string, apiUserInfo DbModels.LineApiUser, retryCount int) (order binancedto.BinanceFutureOrder, err error) { diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 5f2472f..719dc7e 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -127,45 +127,51 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { logger.Errorf("handleReduceFilled 获取交易对设置失败,订单号:%s", preOrder.OrderSn) return } + rate := utility.StringAsFloat(preOrder.Rate) - price := utility.StrToDecimal(preOrder.Price) - parentOrder, err := GetOrderById(db, preOrder.Pid) + // 100%减仓 终止流程 + if rate >= 100 { + //缓存 + removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) - if err != nil { - logger.Errorf("handleReduceFilled 获取主单失败,订单号:%s", preOrder.OrderSn) - return - } - parentPrice := utility.StrToDecimal(parentOrder.Price) - num := utility.StrToDecimal(preOrder.Num) - lossAmount := price.Sub(parentPrice).Abs().Mul(num).Truncate(int32(tradeSet.PriceDigit)) - - if !strings.HasSuffix(preOrder.Symbol, "USDT") { - tradeSetU, err := GetTradeSet(utility.ReplaceSuffix(preOrder.Symbol, preOrder.QuoteSymbol, ""), 1) - - if err != nil { - logger.Errorf("handleMainReduceFilled 获取币本位对应U交易对设置失败,订单号:%s", preOrder.OrderSn) - return + ids := []int{preOrder.MainId, preOrder.Pid} + if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6", ids).Update("status", 9).Error; err != nil { + logger.Info("100%减仓完毕,终结流程") } - - lossAmount = lossAmount.Mul(utility.StrToDecimal(tradeSetU.LastPrice)).Truncate(int32(tradeSetU.PriceDigit)) - } - - if err := db.Model(&parentOrder).Where("loss_amount = ?", 0).Update("loss_amount", lossAmount).Error; err != nil { - logger.Errorf("handleMainReduceFilled 更新亏损金额失败,订单号:%s", preOrder.OrderSn) return } + positionData := savePosition(db, preOrder) orders := make([]models.LinePreOrder, 0) if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status = 0", preOrder.Id).Find(&orders).Error; err != nil { logger.Errorf("handleMainReduceFilled 获取待触发订单失败,订单号:%s", preOrder.OrderSn) return } + orderExt := models.LinePreOrderExt{} totalNum := getFuturesPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) + price := utility.StrToDecimal(preOrder.Price).Truncate(int32(tradeSet.PriceDigit)) futApi := FutRestApi{} + db.Model(&orderExt).Where("order_id =?", preOrder.Pid).First(&orderExt) + for _, v := range orders { if v.OrderType == 1 { + //亏损大于0 重新计算比例 + if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + percentag := positionData.TotalLoss.Div(totalNum).Div(price) + percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + v.Rate = percentag.String() + percentag = percentag.Div(decimal.NewFromInt(100)) + + if positionData.PositionSide == "LONG" { + v.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } else { + v.Price = price.Mul(decimal.NewFromInt(1).Sub(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } + } + processFutTakeProfitOrder(db, futApi, v, totalNum) } else if v.OrderType == 2 { processFutStopLossOrder(db, v, utility.StrToDecimal(v.Price), totalNum) @@ -275,7 +281,8 @@ func getFuturesPositionNum(apiUserInfo DbModels.LineApiUser, preOrder *DbModels. // 平仓单成交 func handleClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeFutLossAndAddPosition(preOrder) + removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) futApi := FutRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) @@ -295,7 +302,8 @@ func handleClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { // 止损单成交 func handleStopLoss(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeFutLossAndAddPosition(preOrder) + removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) spotApi := SpotRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) @@ -319,7 +327,8 @@ func handleStopLoss(db *gorm.DB, preOrder *DbModels.LinePreOrder) { // 止盈单成交 func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeFutLossAndAddPosition(preOrder) + removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) futApi := FutRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) @@ -338,7 +347,7 @@ func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } // 清除合约缓存 -func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { +func removeFutLossAndAddPosition(mainId int, orderSn string) { stoplossKey := fmt.Sprintf(rediskey.FuturesStopLossList, global.EXCHANGE_BINANCE) stoplossVal, _ := helper.DefaultRedis.GetAllList(stoplossKey) addPositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE) @@ -352,11 +361,11 @@ func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //止损缓存 for _, v := range stoplossVal { sonic.Unmarshal([]byte(v), &stoploss) - if stoploss.MainId == preOrder.MainId { + if stoploss.MainId == mainId { _, err := helper.DefaultRedis.LRem(stoplossKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", orderSn, err) } } } @@ -364,11 +373,11 @@ func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //加仓缓存 for _, v := range addPositionVal { sonic.Unmarshal([]byte(v), &addPosition) - if addPosition.MainId == preOrder.MainId { + if addPosition.MainId == mainId { _, err := helper.DefaultRedis.LRem(addPositionKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", orderSn, err) } } } @@ -376,87 +385,172 @@ func removeFutLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //减仓缓存 for _, v := range reduceVal { sonic.Unmarshal([]byte(v), &reduce) - if reduce.MainId == preOrder.MainId { + if reduce.MainId == mainId { _, err := helper.DefaultRedis.LRem(reduceKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", orderSn, err) } } } } -// 主单成交 处理止盈止损订单 -// preOrder 主单 +// 处理主单成交,处理止盈、止损、减仓订单 func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { - orders := []models.LinePreOrder{} - tradeSet, _ := GetTradeSet(preOrder.Symbol, 1) - - if tradeSet.Coin == "" { - logger.Error("获取交易对失败") + // 获取交易对配置和API信息 + tradeSet, err := GetTradeSet(preOrder.Symbol, 1) + if err != nil || tradeSet.Coin == "" { + logger.Errorf("获取交易对配置失败, 回调订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return } - if tradeSet.Coin == "" { - logger.Errorf("获取交易对配置失败, 回调订单号:%s", preOrder.OrderSn) - return - } - - if preOrder.OrderCategory == 3 { - if err := db.Model(&DbModels.LinePreOrderStatus{}).Where("order_id = ?", preOrder.MainId).Update("add_position_status", 1).Error; err != nil { - logger.Errorf("更新主单加仓状态失败, 主单号:%s, 错误信息:%v", preOrder.MainId, err) - } - - if err := cancelSymbolTakeAndStop(db, preOrder.MainId, preOrder.SymbolType); err != nil { - logger.Errorf("取消止盈止损订单失败 orderSn:%s err:%v", preOrder.OrderSn, err) - } - } apiInfo, err := GetApiInfo(preOrder.ApiId) - if apiInfo.Id == 0 { - logger.Error("订单回调查询apiuserinfo失败 err:", err) - return - } - if err := db.Model(&DbModels.LinePreOrder{}). - Where("pid = ? AND order_type > 0 AND status = '0' ", preOrder.Id). - Find(&orders).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { - logger.Error("订单回调查询止盈止损单失败:", err) + if err != nil || apiInfo.Id == 0 { + logger.Errorf("订单回调查询apiuserinfo失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return } - futApi := FutRestApi{} - num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet) - num = num.Truncate(int32(tradeSet.AmountDigit)) + // 处理主单加仓 + if preOrder.OrderCategory == 3 { + if err := handleMainOrderAddPosition(db, preOrder); err != nil { + logger.Errorf("处理主单加仓失败, 主单号:%s, 错误信息: %v", preOrder.MainId, err) + return + } + } else { + // 处理其他主单逻辑 + if err := cancelPositionOtherOrders(apiInfo, db, preOrder); err != nil { + logger.Errorf("取消主单相关订单失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) + return + } + } + // 获取止盈止损订单 + orders, err := getStopOrders(db, preOrder) + if err != nil { + logger.Errorf("查询止盈止损订单失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) + return + } + + // 获取和保存持仓数据 + positionData := savePosition(db, preOrder) + orderExt := models.LinePreOrderExt{} + num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit)) + + // 更新订单数量并处理止盈、止损、减仓 for _, order := range orders { price := utility.StrToDecimal(order.Price).Truncate(int32(tradeSet.PriceDigit)) order.Price = price.String() - if order.OrderType == 4 { - ext := DbModels.LinePreOrderExt{} - db.Model(&ext).Where("order_id=?", preOrder.Id).Find(&ext) + // 更新止盈止损订单数量 + num = updateOrderQuantity(db, order, preOrder, num, tradeSet) - if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { - num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) - order.Num = num.String() - } - } - - if err := db.Model(&order).Update("num", num).Error; err != nil { - logger.Errorf("修改止盈止损数量失败 订单号:%s err:%v", order.OrderSn, err) - } - - logger.Errorf("止盈止损 下单数量:%v", num) + // 根据订单类型处理 switch order.OrderType { case 1: // 止盈 - processFutTakeProfitOrder(db, futApi, order, num) + //亏损大于0 重新计算比例 + if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + percentag := positionData.TotalLoss.Div(num).Div(price) + percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + order.Rate = percentag.String() + percentag = percentag.Div(decimal.NewFromInt(100)) + + if positionData.PositionSide == "LONG" { + order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } else { + order.Price = price.Mul(decimal.NewFromInt(1).Sub(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } + } + + processFutTakeProfitOrder(db, FutRestApi{}, order, num) case 2: // 止损 processFutStopLossOrder(db, order, price, num) - case 4: //减仓 + case 4: // 减仓 processFutReduceOrder(order, price, num) } } } +// 处理主单加仓 +func handleMainOrderAddPosition(db *gorm.DB, preOrder *models.LinePreOrder) error { + // 更新加仓状态 + if err := db.Model(&DbModels.LinePreOrderStatus{}). + Where("order_id = ?", preOrder.MainId). + Update("add_position_status", 1).Error; err != nil { + return err + } + + // 取消止盈止损订单 + return cancelSymbolTakeAndStop(db, preOrder.MainId, preOrder.SymbolType) +} + +// 取消主单相关订单 +func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, preOrder *models.LinePreOrder) error { + mainOrders, err := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) + if err != nil { + return err + } + + mainIds := []int{} + for _, mainOrder := range mainOrders { + removeFutLossAndAddPosition(mainOrder.Id, mainOrder.OrderSn) + mainIds = append(mainIds, mainOrder.Id) + } + + if len(mainIds) > 0 { + orderSns, err := getOpenOrderSns(db, mainIds) + if err != nil { + return err + } + + // 批量取消订单 + orderArray := utility.SplitSlice(orderSns, 10) + futApi := FutRestApi{} + for _, item := range orderArray { + err := futApi.CancelBatchFutOrder(apiUserInfo, preOrder.Symbol, item) + + if err != nil { + logger.Errorf("批量取消订单失败 orderSns:%v", item) + } + } + } + return nil +} + +// 获取止盈止损订单 +func getStopOrders(db *gorm.DB, preOrder *models.LinePreOrder) ([]models.LinePreOrder, error) { + var orders []models.LinePreOrder + if err := db.Model(&DbModels.LinePreOrder{}). + Where("pid = ? AND order_type > 0 AND status = '0' ", preOrder.Id). + Find(&orders).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + return orders, nil +} + +// 更新订单数量 +func updateOrderQuantity(db *gorm.DB, order models.LinePreOrder, preOrder *models.LinePreOrder, num decimal.Decimal, tradeSet models2.TradeSet) decimal.Decimal { + // 处理减仓比例 + if order.OrderType == 4 { + ext := DbModels.LinePreOrderExt{} + if err := db.Model(&ext).Where("order_id=?", preOrder.Id).Find(&ext).Error; err != nil { + logger.Errorf("查询减仓比例失败, 订单号:%s, 错误信息: %v", order.OrderSn, err) + } + + // 计算减仓数量 + if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { + num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + order.Num = num.String() + } + } + + // 更新订单数量 + if err := db.Model(&order).Update("num", num).Error; err != nil { + logger.Errorf("修改止盈止损数量失败 订单号:%s err:%v", order.OrderSn, err) + } + + return num +} + // 减仓单 func processFutReduceOrder(order DbModels.LinePreOrder, price, num decimal.Decimal) { key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE) @@ -510,7 +604,8 @@ func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.Line logger.Error("合约止盈下单失败,更新状态失败:", order.OrderSn, " err:", err) } } else { - if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil { + if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id). + Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate}).Error; err != nil { logger.Error("更新合约止盈单触发事件 ordersn:", order.OrderSn) } diff --git a/services/binanceservice/orderservice.go b/services/binanceservice/orderservice.go index 1d242a3..9fba597 100644 --- a/services/binanceservice/orderservice.go +++ b/services/binanceservice/orderservice.go @@ -154,3 +154,8 @@ func GetSymbolTriggerCount(db *gorm.DB, symbol string, apiId, symbolType int) (i return count, nil } + +//获取已开仓的 +// func GetOpenedOrders(db *gorm.DB, apiId int, exchange, symbol, symbolType, side string) ([]models.LinePreOrder, error) { + +// } diff --git a/services/binanceservice/spotjudgeservice.go b/services/binanceservice/spotjudgeservice.go index da383e4..047e5cd 100644 --- a/services/binanceservice/spotjudgeservice.go +++ b/services/binanceservice/spotjudgeservice.go @@ -83,13 +83,6 @@ func SpotOrderLock(db *gorm.DB, v *dto.PreOrderRedisList, item string, spotApi S return } - //判断是否有已触发交易对 - count, _ := GetSymbolTriggerCount(db, v.Symbol, v.ApiId, 1) - - if count > 0 { - return - } - price, _ := decimal.NewFromString(v.Price) num, _ := decimal.NewFromString(preOrder.Num) params := OrderPlacementService{ diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index 40ce946..f2c2a38 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -11,7 +11,9 @@ import ( "go-admin/common/global" "go-admin/common/helper" models2 "go-admin/models" + "go-admin/models/positiondto" "go-admin/pkg/utility" + "go-admin/services/positionservice" "strconv" "strings" "time" @@ -127,7 +129,8 @@ func handleOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderStatus //主单止损回调 case preOrder.OrderType == 2 && orderStatus == 6: - removeSpotLossAndAddPosition(preOrder) + removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) if err := db.Model(&DbModels.LinePreOrder{}).Where("id =?", preOrder.MainId).Update("status", 9).Error; err != nil { logger.Errorf("主单止损回调 订单号:%s 修改主单状态失败:%v", preOrder.OrderSn, err) @@ -168,6 +171,8 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { return } + orderExt := models.LinePreOrderExt{} + positionData := savePosition(db, preOrder) orders := make([]models.LinePreOrder, 0) rate := utility.StringAsFloat(preOrder.Rate) @@ -177,7 +182,10 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { // 100%减仓 终止流程 if rate >= 100 { - removeSpotLossAndAddPosition(preOrder) + //缓存 + removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) + ids := []int{preOrder.MainId, preOrder.Pid} if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6", ids).Update("status", 9).Error; err != nil { logger.Info("100%减仓完毕,终结流程") @@ -185,7 +193,10 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { return } + db.Model(&orderExt).Where("order_id =?", preOrder.Pid).Find(&orderExt) totalNum := getSpotPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) //getSpotTotalNum(apiUserInfo, preOrder, tradeSet) + totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) + price := utility.StrToDecimal(preOrder.Price) if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status=0", preOrder.Id).Find(&orders).Error; err != nil { logger.Errorf("获取减仓单止盈止损失败 err:%v", err) @@ -195,26 +206,24 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { spotApi := SpotRestApi{} for index := range orders { - orders[index].Num = totalNum.Truncate(int32(tradeSet.AmountDigit)).String() + orders[index].Num = totalNum.String() if orders[index].OrderType == 1 { + //亏损大于0 重新计算比例 + if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + percentag := positionData.TotalLoss.Div(totalNum).Div(price) + percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + orders[index].Rate = percentag.String() + percentag = percentag.Div(decimal.NewFromInt(100)) + orders[index].Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } + processTakeProfitOrder(db, spotApi, orders[index]) } else if orders[index].OrderType == 2 { processStopLossOrder(orders[index]) } } - //计算实际亏损 - // if parentOrder.Price != "" { - // parentPrice := utility.StrToDecimal(parentOrder.Price) - // reduceNum := utility.StrToDecimal(preOrder.Num) - // lossAmountU := price.Sub(parentPrice).Abs().Mul(reduceNum) //.Truncate(int32(tradeSet.PriceDigit)) - - // if err := db.Model(&parentOrder).Update("loss_amount", lossAmountU).Error; err != nil { - // logger.Errorf("修改主单实际亏损失败 订单号:%s err:%v", parentOrder.OrderSn, err) - // } - // } - //加仓待触发 addPositionOrder := DbModels.LinePreOrder{} @@ -376,7 +385,8 @@ func handleMainOrderClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) }) } - removeSpotLossAndAddPosition(preOrder) + removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) spotApi := SpotRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) @@ -387,14 +397,14 @@ func handleMainOrderClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) ApiId: preOrder.ApiId, } if err := spotApi.CancelOpenOrders(db, req); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + logger.Errorf("平仓单成功 取消其它订单失败 订单号:%s:", err) } } } // 止盈成交 func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeSpotLossAndAddPosition(preOrder) + removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) spotApi := SpotRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) @@ -409,6 +419,8 @@ func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } } + removePosition(db, preOrder) + db.Transaction(func(tx *gorm.DB) error { ids := []int{preOrder.Pid, preOrder.MainId} if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6 AND order_type=0", ids).Update("status", 9).Error; err != nil { @@ -428,7 +440,28 @@ func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } -func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { +// 移除仓位信息 +func removePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { + positionMangement := positionservice.BinancePositionManagement{} + positionMangement.Orm = db + positionDelReq := positiondto.LinePreOrderPositioinDelReq{ + ApiId: preOrder.ApiId, + SymbolType: preOrder.SymbolType, + Symbol: preOrder.Symbol, + ExchangeType: preOrder.ExchangeType, + } + + if preOrder.Site == "BUY" { + positionDelReq.Side = "SELL" + } else { + positionDelReq.Side = "BUY" + } + + positionMangement.RemovePosition(&positionDelReq) +} + +// 清理待加仓、待止损、待减仓缓存 +func removeSpotLossAndAddPosition(mainId int, orderSn string) { stoplossKey := fmt.Sprintf(rediskey.SpotStopLossList, global.EXCHANGE_BINANCE) stoplossVal, _ := helper.DefaultRedis.GetAllList(stoplossKey) addPositionKey := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) @@ -442,11 +475,11 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //止损缓存 for _, v := range stoplossVal { sonic.Unmarshal([]byte(v), &stoploss) - if stoploss.MainId == preOrder.MainId { + if stoploss.MainId == mainId { _, err := helper.DefaultRedis.LRem(stoplossKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除止损缓存失败:%v", orderSn, err) } } } @@ -454,11 +487,11 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //加仓缓存 for _, v := range addPositionVal { sonic.Unmarshal([]byte(v), &addPosition) - if addPosition.MainId == preOrder.MainId { + if addPosition.MainId == mainId { _, err := helper.DefaultRedis.LRem(addPositionKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v", orderSn, err) } } } @@ -466,11 +499,11 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { //减仓缓存 for _, v := range reduceVal { sonic.Unmarshal([]byte(v), &reduce) - if reduce.MainId == preOrder.MainId { + if reduce.MainId == mainId { _, err := helper.DefaultRedis.LRem(reduceKey, v) if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", preOrder.OrderSn, err) + logger.Errorf("订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v", orderSn, err) } } } @@ -478,13 +511,31 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { // 主单成交 func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { + //修改持仓信息 + positionData := savePosition(db, preOrder) + if preOrder.OrderCategory == 3 { if err := db.Model(&DbModels.LinePreOrderStatus{}).Where("order_id = ?", preOrder.MainId).Update("add_position_status", 1).Error; err != nil { logger.Errorf("更新主单加仓状态失败, 主单号:%s, 错误信息:%v", preOrder.MainId, err) } + } else { + mainOrders, _ := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) + + if len(mainOrders) > 0 { + for _, mainOrder := range mainOrders { + removeSpotLossAndAddPosition(mainOrder.Id, mainOrder.OrderSn) + } + + spotApi := SpotRestApi{} + err := spotApi.CancelOpenOrdersLoop(db, CancelOpenOrdersReq{ApiId: preOrder.ApiId, Symbol: preOrder.Symbol}, 4) + + if err != nil { + logger.Errorf("取消未成交订单失败, 交易对:%s 主单号:%s, 错误信息:%v", preOrder.Symbol, preOrder.MainId, err) + } + } } - processTakeProfitAndStopLossOrders(db, preOrder) + processTakeProfitAndStopLossOrders(db, preOrder, &positionData) } // 解析订单状态 @@ -551,7 +602,8 @@ func updateOrderStatus(db *gorm.DB, preOrder *models.LinePreOrder, status int, r // 主单成交 处理止盈止损订单 // preOrder 主单 -func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder) { +// positionData 持仓缓存信息 +func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder, positionData *positiondto.PositionDto) { orders := []models.LinePreOrder{} tradeSet, _ := GetTradeSet(preOrder.Symbol, 0) @@ -581,7 +633,10 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd return } + price := utility.StrToDecimal(preOrder.Price) spotApi := SpotRestApi{} + orderExt := models.LinePreOrderExt{} + db.Model(&orderExt).Where("order_id =?", preOrder.Id).First(&orderExt) for _, order := range orders { order.Num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)).String() @@ -601,6 +656,15 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd switch order.OrderType { case 1: // 止盈 + //亏损大于0 重新计算比例 + if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + percentag := positionData.TotalLoss.Div(num).Div(price) + percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + order.Rate = percentag.String() + percentag = percentag.Div(decimal.NewFromInt(100)) + order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } + processTakeProfitOrder(db, spotApi, order) case 2: // 止损 processStopLossOrder(order) @@ -686,7 +750,8 @@ func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LineP logger.Error("现货止盈下单失败,更新状态失败:", order.OrderSn, " err:", err) } } else { - if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil { + if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id). + Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate}).Error; err != nil { logger.Error("更新现货止盈单触发事件 ordersn:", order.OrderSn) } if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status ='0'", order.Id). diff --git a/services/positionservice/position_management.go b/services/positionservice/position_management.go new file mode 100644 index 0000000..dd74aa4 --- /dev/null +++ b/services/positionservice/position_management.go @@ -0,0 +1,120 @@ +package positionservice + +import ( + "context" + "fmt" + "go-admin/common/const/rediskey" + "go-admin/common/helper" + "go-admin/common/service" + "go-admin/models/positiondto" + "time" + + "github.com/bytedance/sonic" + "github.com/go-admin-team/go-admin-core/logger" + "github.com/shopspring/decimal" +) + +type BinancePositionManagement struct { + service.Service +} + +// 保存仓位信息 +// return 仓位信息 +// return 错误信息 +func (e *BinancePositionManagement) SavePosition(data *positiondto.PositionAddReq, exchangeType string) (positiondto.PositionDto, error) { + var key string + result := positiondto.PositionDto{} + + switch data.SymbolType { + case 1: + key = fmt.Sprintf(rediskey.SpotPosition, exchangeType, data.ApiId, data.Symbol, data.Side) + case 2: + key = fmt.Sprintf(rediskey.FuturePosition, exchangeType, data.ApiId, data.Symbol, data.Side) + default: + return result, fmt.Errorf("symbol type error") + } + + lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotPositionLock, data.ApiId, data.Symbol, data.Side), 200, 5, 200*time.Millisecond) + + if ok, err := lock.AcquireWait(context.Background()); err != nil { + logger.Debug("获取锁失败", err) + return result, err + } else if ok { + defer lock.Release() + val, _ := helper.DefaultRedis.GetString(key) + + if val != "" { + sonic.Unmarshal([]byte(val), &result) + } + + if result.Symbol == "" { + result.Symbol = data.Symbol + result.Side = data.Side + result.ApiId = data.ApiId + result.SymbolType = data.SymbolType + result.PositionSide = data.PositionSide + } + + var totalLoss decimal.Decimal + + if result.LastPrice.Cmp(decimal.Zero) > 0 { + switch { + //多 买入 + case data.PositionSide == "LONG": + totalLoss = result.LastPrice.Sub(data.Price).Abs().Mul(result.Quantity) + + case data.PositionSide == "SHORT": + totalLoss = data.Price.Sub(result.LastPrice).Abs().Mul(result.Quantity) + } + } + + result.LastPrice = data.Price + result.TotalLoss = result.TotalLoss.Add(totalLoss) + result.Quantity = data.Quantity.Add(result.Quantity) + + dataVal, _ := sonic.MarshalString(result) + if err := helper.DefaultRedis.SetString(key, dataVal); err != nil { + logger.Errorf("保存仓位信息失败,val:%s err:%v", dataVal, err) + } + } + + return result, nil +} + +// 获取系统内仓位信息 +func (e *BinancePositionManagement) GetPosition(apiId, symbolType int, exchangeTyp, symbol, side string) (positiondto.PositionDto, error) { + result := positiondto.PositionDto{} + var key string + + switch symbolType { + case 1: + key = fmt.Sprintf(rediskey.SpotPosition, exchangeTyp, apiId, symbol, side) + case 2: + key = fmt.Sprintf(rediskey.FuturePosition, exchangeTyp, apiId, symbol, side) + default: + return result, fmt.Errorf("symbol type error") + } + + val, _ := helper.DefaultRedis.GetString(key) + if val != "" { + sonic.Unmarshal([]byte(val), &result) + } + + return result, nil +} + +// 移除仓位信息 +func (e *BinancePositionManagement) RemovePosition(req *positiondto.LinePreOrderPositioinDelReq) error { + var key string + + switch req.SymbolType { + case 1: + key = fmt.Sprintf(rediskey.SpotPosition, req.ExchangeType, req.ApiId, req.Symbol, req.Side) + case 2: + key = fmt.Sprintf(rediskey.FuturePosition, req.ExchangeType, req.ApiId, req.Symbol, req.Side) + default: + return fmt.Errorf("symbol type error") + } + + return helper.DefaultRedis.DeleteString(key) +} From ca92638554e74fe782f6dbd1cd86277e6f903648 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Mon, 3 Mar 2025 17:35:41 +0800 Subject: [PATCH 07/13] =?UTF-8?q?1=E3=80=81=E7=B4=AF=E8=AE=A1=E4=BA=8F?= =?UTF-8?q?=E6=8D=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/binanceservice/commonservice.go | 18 ++++++++---- .../binanceservice/futuresjudgeservice.go | 7 ----- services/binanceservice/futuresrest.go | 18 +++++++++--- services/binanceservice/spotreset.go | 29 ++++++++++++++----- .../positionservice/position_management.go | 21 ++++++++++---- 5 files changed, 65 insertions(+), 28 deletions(-) diff --git a/services/binanceservice/commonservice.go b/services/binanceservice/commonservice.go index d554c8c..091f1c6 100644 --- a/services/binanceservice/commonservice.go +++ b/services/binanceservice/commonservice.go @@ -422,8 +422,8 @@ func savePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) positiondto.Posi SymbolType: preOrder.SymbolType, Symbol: preOrder.Symbol, Price: utility.StrToDecimal(preOrder.Price), - Side: preOrder.Site, - Quantity: utility.StrToDecimal(preOrder.Num), + // Side: preOrder.Site, + Quantity: utility.StrToDecimal(preOrder.Num), } switch { @@ -436,8 +436,16 @@ func savePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) positiondto.Posi } //减仓单 数量为负 - if preOrder.OrderType == 4 { + if preOrder.OrderType != 0 { + if preOrder.Site == "BUY" { + positionReq.Side = "SELL" + } else { + positionReq.Side = "BUY" + } + positionReq.Quantity = positionReq.Quantity.Mul(decimal.NewFromInt(-1)) + } else { + positionReq.Side = preOrder.Site } positionData, err := positionManage.SavePosition(&positionReq, global.EXCHANGE_BINANCE) @@ -453,9 +461,9 @@ func getOpenPositionMainOrderId(db *gorm.DB, newId, apiId, symbolType int, excha mainOrders := make([]DbModels.LinePreOrder, 0) if err := db.Model(&DbModels.LinePreOrder{}). - Where("api_id =? AND status>4 AND status<7 AND symbol=? AND symbol_type =? AND side= ? AND exchange_type=? AND id!=?", + Where("api_id =? AND status>4 AND status<7 AND symbol=? AND symbol_type =? AND site= ? AND exchange_type=? AND id!=?", apiId, symbol, symbolType, side, exchangeType, newId). - Select("id", "order_sn").Find(&mainOrders).Error; err != nil { + Select("id", "main_id", "order_sn").Find(&mainOrders).Error; err != nil { return nil, err } diff --git a/services/binanceservice/futuresjudgeservice.go b/services/binanceservice/futuresjudgeservice.go index 2966fc7..1c098ce 100644 --- a/services/binanceservice/futuresjudgeservice.go +++ b/services/binanceservice/futuresjudgeservice.go @@ -87,13 +87,6 @@ func futTriggerOrder(db *gorm.DB, v *dto.PreOrderRedisList, item string, futApi return } - //判断是否有已触发交易对 - count, _ := GetSymbolTriggerCount(db, v.Symbol, v.ApiId, 2) - - if count > 0 { - return - } - price, _ := decimal.NewFromString(v.Price) num, _ := decimal.NewFromString(preOrder.Num) diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 719dc7e..5e7d289 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -160,7 +160,7 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { if v.OrderType == 1 { //亏损大于0 重新计算比例 if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { - percentag := positionData.TotalLoss.Div(totalNum).Div(price) + percentag := positionData.TotalLoss.Div(totalNum).Div(price).Mul(decimal.NewFromInt(100)) percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) v.Rate = percentag.String() percentag = percentag.Div(decimal.NewFromInt(100)) @@ -434,6 +434,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { // 获取和保存持仓数据 positionData := savePosition(db, preOrder) orderExt := models.LinePreOrderExt{} + db.Model(&orderExt).Where("order_id =?", preOrder.Id).First(&orderExt) num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit)) // 更新订单数量并处理止盈、止损、减仓 @@ -449,7 +450,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { case 1: // 止盈 //亏损大于0 重新计算比例 if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { - percentag := positionData.TotalLoss.Div(num).Div(price) + percentag := positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)) percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) order.Rate = percentag.String() percentag = percentag.Div(decimal.NewFromInt(100)) @@ -492,8 +493,13 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr mainIds := []int{} for _, mainOrder := range mainOrders { - removeFutLossAndAddPosition(mainOrder.Id, mainOrder.OrderSn) - mainIds = append(mainIds, mainOrder.Id) + mainId := mainOrder.Id + + if mainOrder.MainId > 0 { + mainId = mainOrder.MainId + } + removeFutLossAndAddPosition(mainId, mainOrder.OrderSn) + mainIds = append(mainIds, mainId) } if len(mainIds) > 0 { @@ -512,6 +518,10 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr logger.Errorf("批量取消订单失败 orderSns:%v", item) } } + + if err := db.Exec("UPDATE line_pre_order SET `status`=4, `desc`=CONCAT(`desc`, ' 新单触发取消') WHERE id IN ? AND `status` =6", mainIds).Error; err != nil { + logger.Errorf("合约 新下单成功后更新主单取消状态失败, 新主单号:%s, 错误信息:%v", preOrder.MainId, err) + } } return nil } diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index f2c2a38..dfbc5e5 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -211,7 +211,7 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { if orders[index].OrderType == 1 { //亏损大于0 重新计算比例 if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { - percentag := positionData.TotalLoss.Div(totalNum).Div(price) + percentag := positionData.TotalLoss.Div(totalNum).Div(price).Mul(decimal.NewFromInt(100)) percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) orders[index].Rate = percentag.String() percentag = percentag.Div(decimal.NewFromInt(100)) @@ -429,11 +429,11 @@ func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { return err } - if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND status=0").Update("status", 4).Error; err != nil { - logger.Errorf("止盈订单回调失败, 回调订单号:%s 更新取消状态失败:%v", preOrder.OrderSn, err) + // if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND status=0",).Update("status", 4).Error; err != nil { + // logger.Errorf("止盈订单回调失败, 回调订单号:%s 更新取消状态失败:%v", preOrder.OrderSn, err) - return err - } + // return err + // } return nil }) @@ -522,8 +522,19 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { mainOrders, _ := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) if len(mainOrders) > 0 { + mainIds := []int{} for _, mainOrder := range mainOrders { - removeSpotLossAndAddPosition(mainOrder.Id, mainOrder.OrderSn) + mainId := mainOrder.Id + + if mainOrder.MainId > 0 { + mainId = mainOrder.MainId + } + + removeSpotLossAndAddPosition(mainId, mainOrder.OrderSn) + + if !utility.ContainsInt(mainIds, mainId) { + mainIds = append(mainIds, mainId) + } } spotApi := SpotRestApi{} @@ -531,6 +542,10 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { if err != nil { logger.Errorf("取消未成交订单失败, 交易对:%s 主单号:%s, 错误信息:%v", preOrder.Symbol, preOrder.MainId, err) + } else if len(mainIds) > 0 { + if err := db.Exec("UPDATE line_pre_order SET status=4,desc=desc +' 新单触发取消' WHERE id IN ? AND status =6", mainIds).Error; err != nil { + logger.Errorf("新下单成功后更新主单取消状态失败, 新主单号:%s, 错误信息:%v", preOrder.MainId, err) + } } } } @@ -658,7 +673,7 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd case 1: // 止盈 //亏损大于0 重新计算比例 if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { - percentag := positionData.TotalLoss.Div(num).Div(price) + percentag := positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)) percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) order.Rate = percentag.String() percentag = percentag.Div(decimal.NewFromInt(100)) diff --git a/services/positionservice/position_management.go b/services/positionservice/position_management.go index dd74aa4..78221c3 100644 --- a/services/positionservice/position_management.go +++ b/services/positionservice/position_management.go @@ -56,20 +56,31 @@ func (e *BinancePositionManagement) SavePosition(data *positiondto.PositionAddRe } var totalLoss decimal.Decimal - if result.LastPrice.Cmp(decimal.Zero) > 0 { switch { //多 买入 case data.PositionSide == "LONG": - totalLoss = result.LastPrice.Sub(data.Price).Abs().Mul(result.Quantity) + totalLoss = result.LastPrice.Sub(data.Price).Mul(result.Quantity) + if data.Price.Cmp(result.LastPrice) < 0 { + result.LastPrice = data.Price + } case data.PositionSide == "SHORT": - totalLoss = data.Price.Sub(result.LastPrice).Abs().Mul(result.Quantity) + totalLoss = data.Price.Sub(result.LastPrice).Mul(result.Quantity) + + if data.Price.Cmp(result.LastPrice) > 0 { + result.LastPrice = data.Price + } } + } else { + //默认没有金额的时候 + result.LastPrice = data.Price + } + + if totalLoss.Cmp(decimal.Zero) > 0 { + result.TotalLoss = result.TotalLoss.Add(totalLoss) } - result.LastPrice = data.Price - result.TotalLoss = result.TotalLoss.Add(totalLoss) result.Quantity = data.Quantity.Add(result.Quantity) dataVal, _ := sonic.MarshalString(result) From 981d2c0108bc1e772ec09b738e37620c4c741c41 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Tue, 4 Mar 2025 15:34:32 +0800 Subject: [PATCH 08/13] =?UTF-8?q?1=E3=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/binanceservice/futuresrest.go | 11 +++++++---- services/binanceservice/spotreset.go | 4 ++-- services/positionservice/position_management.go | 5 ++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 5e7d289..8c32a08 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -261,10 +261,10 @@ func getFuturesPositionNum(apiUserInfo DbModels.LineApiUser, preOrder *DbModels. positionAmt := utility.StrToDecimal(item.PositionAmt) //多 - if positionAmt.Cmp(decimal.Zero) > 0 && preOrder.Site == "SELL" { + if positionAmt.Cmp(decimal.Zero) > 0 && ((preOrder.OrderType == 0 && preOrder.Site == "BUY") || (preOrder.OrderType != 0 && preOrder.Site == "SELL")) { num = positionAmt.Abs().Truncate(int32(tradeSet.AmountDigit)) break - } else if positionAmt.Cmp(decimal.Zero) < 0 && preOrder.Site == "BUY" { + } else if positionAmt.Cmp(decimal.Zero) < 0 && ((preOrder.OrderType != 0 && preOrder.Site == "BUY") || (preOrder.OrderType == 0 && preOrder.Site == "SELL")) { //空 num = positionAmt.Abs().Truncate(int32(tradeSet.AmountDigit)) break @@ -499,7 +499,10 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr mainId = mainOrder.MainId } removeFutLossAndAddPosition(mainId, mainOrder.OrderSn) - mainIds = append(mainIds, mainId) + + if !utility.ContainsInt(mainIds, mainId) { + mainIds = append(mainIds, mainId) + } } if len(mainIds) > 0 { @@ -620,7 +623,7 @@ func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.Line } if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status =0", order.Id). - Updates(map[string]interface{}{"status": "1", "num": num.String()}).Error; err != nil { + Updates(map[string]interface{}{"status": "1", "num": num.String(), "price": order.Price}).Error; err != nil { logger.Error("合约止盈下单成功,更新状态失败:", order.OrderSn, " err:", err) } } diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index dfbc5e5..3cdbb45 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -543,7 +543,7 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { if err != nil { logger.Errorf("取消未成交订单失败, 交易对:%s 主单号:%s, 错误信息:%v", preOrder.Symbol, preOrder.MainId, err) } else if len(mainIds) > 0 { - if err := db.Exec("UPDATE line_pre_order SET status=4,desc=desc +' 新单触发取消' WHERE id IN ? AND status =6", mainIds).Error; err != nil { + if err := db.Exec("UPDATE line_pre_order SET `status`=4,`desc`=CONCAT(`desc`, ' 新单触发取消') WHERE id IN ? AND `status` =6", mainIds).Error; err != nil { logger.Errorf("新下单成功后更新主单取消状态失败, 新主单号:%s, 错误信息:%v", preOrder.MainId, err) } } @@ -770,7 +770,7 @@ func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LineP logger.Error("更新现货止盈单触发事件 ordersn:", order.OrderSn) } if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status ='0'", order.Id). - Updates(map[string]interface{}{"status": "1", "num": order.Num}).Error; err != nil { + Updates(map[string]interface{}{"status": "1", "num": order.Num, "price": order.Price}).Error; err != nil { logger.Error("现货止盈下单成功,更新状态失败:", order.OrderSn, " err:", err) } } diff --git a/services/positionservice/position_management.go b/services/positionservice/position_management.go index 78221c3..b44e050 100644 --- a/services/positionservice/position_management.go +++ b/services/positionservice/position_management.go @@ -77,8 +77,11 @@ func (e *BinancePositionManagement) SavePosition(data *positiondto.PositionAddRe result.LastPrice = data.Price } - if totalLoss.Cmp(decimal.Zero) > 0 { + //总亏损大于0 + if totalLoss.Add(result.TotalLoss).Cmp(decimal.Zero) > 0 { result.TotalLoss = result.TotalLoss.Add(totalLoss) + } else { + result.TotalLoss = decimal.Zero } result.Quantity = data.Quantity.Add(result.Quantity) From 126193df36739fb8a12f3f1f730dac37debc6bb6 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Thu, 6 Mar 2025 18:16:35 +0800 Subject: [PATCH 09/13] =?UTF-8?q?1=E3=80=81=E4=BA=A4=E6=98=93=E5=AF=B9?= =?UTF-8?q?=E9=BB=91=E5=90=8D=E5=8D=95=20=E4=BA=A4=E6=98=93=E6=89=80?= =?UTF-8?q?=E4=B8=8B=E6=9E=B6=E7=9A=84=E4=BA=A4=E6=98=93=E5=AF=B9=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/apis/line_pre_order.go | 53 +- app/admin/apis/line_symbol_black.go | 28 + app/admin/models/line_pre_order_ext.go | 31 +- app/admin/router/line_pre_order.go | 4 +- app/admin/router/line_symbol_black.go | 6 +- app/admin/service/dto/line_pre_order.go | 107 ++-- app/admin/service/dto/line_pre_order_ext.go | 88 +-- app/admin/service/line_pre_order.go | 668 +++++++++++--------- app/admin/service/line_symbol.go | 67 +- app/admin/service/line_symbol_black.go | 19 +- services/binanceservice/commonservice.go | 2 +- services/binanceservice/futuresrest.go | 89 ++- services/binanceservice/orderservice.go | 11 + services/binanceservice/spotreset.go | 101 +-- 14 files changed, 731 insertions(+), 543 deletions(-) diff --git a/app/admin/apis/line_pre_order.go b/app/admin/apis/line_pre_order.go index 40a2c1f..1f18111 100644 --- a/app/admin/apis/line_pre_order.go +++ b/app/admin/apis/line_pre_order.go @@ -306,34 +306,34 @@ func (e LinePreOrder) AddPreOrder(c *gin.Context) { e.OK(nil, "操作成功") } -// 手动加仓 -func (e LinePreOrder) AddPosition(c *gin.Context) { - s := service.LinePreOrder{} - req := dto.LinePreOrderAddPositionReq{} - 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 - } +// // 手动加仓 +// func (e LinePreOrder) AddPosition(c *gin.Context) { +// s := service.LinePreOrder{} +// req := dto.LinePreOrderAddPositionReq{} +// 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 +// } - if err := req.Valid(); err != nil { - e.Error(500, err, err.Error()) - return - } +// if err := req.Valid(); err != nil { +// e.Error(500, err, err.Error()) +// return +// } - err = s.AddPosition(&req) +// err = s.AddPosition(&req) - if err != nil { - e.Error(500, nil, err.Error()) - return - } - e.OK(nil, "操作成功") -} +// if err != nil { +// e.Error(500, nil, err.Error()) +// return +// } +// e.OK(nil, "操作成功") +// } // BatchAddOrder 批量添加 func (e LinePreOrder) BatchAddOrder(c *gin.Context) { @@ -654,8 +654,7 @@ func (e LinePreOrder) CalculateBreakEevenRatio(c *gin.Context) { return } - // data := dto.CalculateBreakEvenRatioResp{} - _, err = s.GenerateOrder(&req) + err = s.GenerateOrder(&req) if err != nil { e.Error(500, err, err.Error()) return diff --git a/app/admin/apis/line_symbol_black.go b/app/admin/apis/line_symbol_black.go index b8ca9a4..9659f94 100644 --- a/app/admin/apis/line_symbol_black.go +++ b/app/admin/apis/line_symbol_black.go @@ -192,3 +192,31 @@ func (e LineSymbolBlack) Delete(c *gin.Context) { } e.OK(req.GetId(), "删除成功") } + +// 重置交易对 +func (e LineSymbolBlack) RelodSymbol(c *gin.Context) { + s := service.LineSymbolBlack{} + err := e.MakeContext(c). + MakeOrm(). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + + err = s.ReloadSymbol("1") + if err != nil { + e.Error(500, err, fmt.Sprintf("重置现货交易对失败,\r\n失败信息 %s", err.Error())) + return + } + + err = s.ReloadSymbol("2") + if err != nil { + e.Error(500, err, fmt.Sprintf("重置合约交易对失败,\r\n失败信息 %s", err.Error())) + return + } + + e.OK(nil, "重置成功") +} diff --git a/app/admin/models/line_pre_order_ext.go b/app/admin/models/line_pre_order_ext.go index 308e109..9836848 100644 --- a/app/admin/models/line_pre_order_ext.go +++ b/app/admin/models/line_pre_order_ext.go @@ -9,22 +9,21 @@ import ( type LinePreOrderExt struct { models.Model - MainOrderId int `json:"mainOrderId" gorm:"type:bigint;comment:主单id"` - OrderId int `json:"orderId" gorm:"type:bigint;comment:订单id"` - TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" gorm:"type:decimal(10,2);comment:止盈百分比"` - ReTakeRatio decimal.Decimal `json:"reTakeRatio" gorm:"type:decimal(10,2);comment:亏损回本止盈百分比"` - ReduceOrderType string `json:"reduceOrderType" gorm:"type:varchar(20);comment:减仓类型 LIMIT-限价 MARKET-市价"` - ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" gorm:"type:decimal(10,2);comment:减仓价格百分比"` - ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" gorm:"type:decimal(10,2);comment:减仓数量百分比"` - ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" gorm:"type:decimal(10,2);comment:减仓后止盈百分比"` - ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" gorm:"type:decimal(10,2);comment:减仓后止损百分比"` - AddPositionOrderType string `json:"addPositionOrderType" gorm:"type:varchar(20);comment:加仓类型 LIMIT-限价 MARKET-市价"` - AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" gorm:"type:decimal(10,2);comment:加仓价格百分比"` - AddPositionType int `json:"addPositionType" gorm:"type:int;comment:加仓类型 1-百分比 2-实际金额"` - AddPositionVal decimal.Decimal `json:"addPositionVal" gorm:"type:decimal(10,2);comment:加仓值"` - ReduceReTakeRatio decimal.Decimal `json:"reduceReTakeRatio" gorm:"type:decimal(10,2);comment:减仓后亏损回本止盈百分比"` - TotalAfterAdding decimal.Decimal `json:"totalAfterAdding" gorm:"-"` //加仓后总数 - TotalAfterReducing decimal.Decimal `json:"totalAfterReducing" gorm:"-"` //减仓后总数 + MainOrderId int `json:"mainOrderId" gorm:"type:bigint;comment:主单id"` + OrderId int `json:"orderId" gorm:"type:bigint;comment:订单id"` + AddType int `json:"addType" gorm:"type:tinyint;comment:类型 1-加仓 2-减仓"` + OrderType string `json:"orderType" gorm:"type:varchar(20);comment:订单类型 LIMIT-限价 MARKET-市价"` + PriceRatio decimal.Decimal `json:"priceRatio" gorm:"type:decimal(10,2);comment: (加仓/减仓)触发价格百分比"` + AddPositionType int `json:"addPositionType" gorm:"type:int;comment:(加仓/减仓)类型 1-百分比 2-实际金额"` + AddPositionVal decimal.Decimal `json:"addPositionVal" gorm:"type:decimal(10,2);comment:加仓值"` + TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" gorm:"type:decimal(10,2);comment:止盈百分比"` + StopLossRatio decimal.Decimal `json:"stopLossRatio" gorm:"type:decimal(10,2);comment:止损百分比"` + TakeProfitNumRatio decimal.Decimal `json:"takeProfitNumRatio" gorm:"type:decimal(10,2);comment:止盈数量百分比"` + TpTpPriceRatio decimal.Decimal `json:"tpTpPriceRatio" gorm:"type:decimal(10,2);comment:止盈后止盈百分比"` + TpSlPriceRatio decimal.Decimal `json:"tpSlPriceRatio" gorm:"type:decimal(10,2);comment:止盈后止损百分比"` + ReTakeRatio decimal.Decimal `json:"reTakeRatio" gorm:"type:decimal(10,2);comment:亏损回本止盈百分比"` + TotalBefore decimal.Decimal `gorm:"-" comment:"初始总数"` + TotalAfter decimal.Decimal `gorm:"-" comment:"剩余总数"` models.ModelTime models.ControlBy } diff --git a/app/admin/router/line_pre_order.go b/app/admin/router/line_pre_order.go index d1c759f..d912362 100644 --- a/app/admin/router/line_pre_order.go +++ b/app/admin/router/line_pre_order.go @@ -25,8 +25,8 @@ func registerLinePreOrderRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTM r.PUT("/:id", actions.PermissionAction(), api.Update) r.DELETE("", api.Delete) - r.POST("addOrder", actions.PermissionAction(), api.AddPreOrder) //添加订单 - r.POST("position", actions.PermissionAction(), api.AddPosition) //手动加仓 + r.POST("addOrder", actions.PermissionAction(), api.AddPreOrder) //添加订单 + // r.POST("position", actions.PermissionAction(), api.AddPosition) //手动加仓 r.POST("batchAddOrder", actions.PermissionAction(), api.BatchAddOrder) //批量添加订单 r.POST("quickAddPreOrder", actions.PermissionAction(), api.QuickAddPreOrder) //快捷下单 r.POST("lever", actions.PermissionAction(), api.Lever) //设置杠杆 diff --git a/app/admin/router/line_symbol_black.go b/app/admin/router/line_symbol_black.go index 681a0b7..9990e59 100644 --- a/app/admin/router/line_symbol_black.go +++ b/app/admin/router/line_symbol_black.go @@ -5,8 +5,8 @@ import ( 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" + "go-admin/common/middleware" ) func init() { @@ -23,5 +23,7 @@ func registerLineSymbolBlackRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJ r.POST("", api.Insert) r.PUT("/:id", actions.PermissionAction(), api.Update) r.DELETE("", api.Delete) + + r.GET("reload-symbol", api.RelodSymbol) } -} \ No newline at end of file +} diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index a4e3801..2199b32 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -2,6 +2,7 @@ package dto import ( "errors" + "fmt" "strconv" "go-admin/app/admin/models" @@ -182,27 +183,29 @@ func (s *LinePreOrderDeleteReq) GetId() interface{} { } type LineAddPreOrderReq struct { - ExchangeType string `json:"exchange_type" vd:"len($)>0"` //交易所类型 - OrderType int `json:"order_type"` //订单类型 - Symbol string `json:"symbol"` //交易对 - ApiUserId string `json:"api_id" ` //下单用户 - Site string `json:"site" ` //购买方向 - BuyPrice string `json:"buy_price" vd:"$>0"` //购买金额 U - PricePattern string `json:"price_pattern"` //价格模式 - Price string `json:"price" vd:"$>0"` //下单价百分比 - Profit string `json:"profit" vd:"$>0"` //止盈价 - // StopPrice string `json:"stop_price"` //止损价 - PriceType string `json:"price_type"` //价格类型 - SaveTemplate string `json:"save_template"` //是否保存模板 - TemplateName string `json:"template_name"` //模板名字 - SymbolType int `json:"symbol_type" vd:"$>0"` //交易对类型 1-现货 2-合约 - CoverType int `json:"cover_type"` //对冲类型 0=无对冲 1= 现货对合约 2=合约对合约 3 合约对现货 - ExpireHour int `json:"expire_hour"` // 过期时间 单位小时 - MainOrderType string `json:"main_order_type" ` //主单类型:限价(LIMIT)或市价(MARKET) - ReducePriceRatio decimal.Decimal `json:"reduce_price" ` //主单减仓价格百分比 - ReduceNumRatio decimal.Decimal `json:"reduce_num" ` //主单减仓数量百分比 - ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比 - ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比 + ExchangeType string `json:"exchange_type" vd:"len($)>0"` //交易所类型 + OrderType int `json:"order_type"` //订单类型 + Symbol string `json:"symbol"` //交易对 + ApiUserId string `json:"api_id" ` //下单用户 + Site string `json:"site" ` //购买方向 + BuyPrice string `json:"buy_price" vd:"$>0"` //购买金额 U + PricePattern string `json:"price_pattern"` //价格模式 + Price string `json:"price" vd:"$>0"` //下单价百分比 + Profit string `json:"profit" vd:"$>0"` //止盈价 + ProfitNumRatio decimal.Decimal `json:"profit_num_ratio"` //止盈数量百分比 + ProfitTpTpPriceRatio decimal.Decimal `json:"profit_tp_tp_price_ratio"` //止盈后止盈价百分比 + ProfitTpSlPriceRatio decimal.Decimal `json:"profit_tp_sl_price_ratio"` //止盈后止损价百分比 + PriceType string `json:"price_type"` //价格类型 + SaveTemplate string `json:"save_template"` //是否保存模板 + TemplateName string `json:"template_name"` //模板名字 + SymbolType int `json:"symbol_type" vd:"$>0"` //交易对类型 1-现货 2-合约 + CoverType int `json:"cover_type"` //对冲类型 0=无对冲 1= 现货对合约 2=合约对合约 3 合约对现货 + ExpireHour int `json:"expire_hour"` // 过期时间 单位小时 + MainOrderType string `json:"main_order_type" ` //主单类型:限价(LIMIT)或市价(MARKET) + ReducePriceRatio decimal.Decimal `json:"reduce_price" ` //主单减仓价格百分比 + ReduceNumRatio decimal.Decimal `json:"reduce_num" ` //主单减仓数量百分比 + ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比 + ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比 ReduceReTakeProfitRatio decimal.Decimal `json:"re_take_profit_ratio" comment:"减仓后亏损回本止盈百分比"` Ext []LineAddPreOrderExtReq `json:"ext" ` //拓展字段 @@ -275,23 +278,33 @@ func (req LineAddPreOrderReq) Valid() error { } for _, v := range req.Ext { + name := "加仓" + + if v.AddType < 1 || v.AddType > 2 { + return errors.New("加、减仓单类型错误") + } + + if v.AddType == 2 { + name = "减仓" + } + if v.AddPositionVal.IsZero() { - return errors.New("加仓单数量不能为空") + return fmt.Errorf("%s单数量不能为空", name) } - if v.AddPositionPriceRatio.IsZero() { - return errors.New("加仓单下跌价格不能为空") + if v.PriceRatio.Cmp(decimal.Zero) <= 0 || v.PriceRatio.Cmp(decimal.NewFromInt(100)) >= 0 { + return fmt.Errorf("%s单下跌价格不能为空", name) } - if v.ReduceNumRatio.IsZero() || v.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓数量不正确") + if v.TakeProfitRatio.IsZero() || v.TakeProfitRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈价格不正确") } - if v.ReducePriceRatio.IsZero() || v.ReducePriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓下跌价格不正确") + if v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈后止盈价格不正确") } - if v.ReduceTakeProfitRatio.IsZero() || v.ReduceTakeProfitRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓后止盈价格不正确") + if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈后止损价格不正确") } } @@ -392,23 +405,33 @@ func (req LineBatchAddPreOrderReq) CheckParams() error { } for _, v := range req.Ext { + name := "加仓" + + if v.AddType < 1 || v.AddType > 2 { + return errors.New("加、减仓单类型错误") + } + + if v.AddType == 2 { + name = "减仓" + } + if v.AddPositionVal.IsZero() { - return errors.New("加仓单数量不能为空") + return fmt.Errorf("%s单数量不能为空", name) } - if v.AddPositionPriceRatio.IsZero() { - return errors.New("加仓单下跌价格不能为空") + if v.PriceRatio.Cmp(decimal.Zero) <= 0 || v.PriceRatio.Cmp(decimal.NewFromInt(100)) >= 0 { + return fmt.Errorf("%s单下跌价格不能为空", name) } - if v.ReduceNumRatio.IsZero() || v.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓数量不正确") + if v.TakeProfitRatio.IsZero() || v.TakeProfitRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈价格不正确") } - if v.ReducePriceRatio.IsZero() || v.ReducePriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓下跌价格不正确") + if v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈后止盈价格不正确") } - if v.ReduceTakeProfitRatio.IsZero() || v.ReduceTakeProfitRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("减仓后止盈价格不正确") + if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + return errors.New("止盈后止损价格不正确") } } @@ -579,12 +602,12 @@ type CalculateBreakEevenRatioReq struct { Symbol string `form:"symbol"` //交易对 ExchangeType string `form:"exchangeType"` //交易所类型 字典exchange_type SymbolType int `form:"symbolType"` + AddType int `form:"addType" comment:"类型 1-加仓 2-减仓"` BuyPrice decimal.Decimal `form:"buyPrice"` //主单购买总金额 LossBeginPercent decimal.Decimal `form:"lossBeginPercent"` //亏损开始百分比 LossEndPercent decimal.Decimal `form:"lossEndPercent"` //亏损截至百分比 - AddPositionType int `form:"addPositionType"` //加仓类型 1-百分比 2-实际金额 - AddPositionVal decimal.Decimal `form:"addPositionVal"` //加仓金额 - ReducePercent decimal.Decimal `form:"reducePercent"` //减仓百分比 + AddPositionType int `form:"addPositionType"` //加仓/减仓 类型 1-百分比 2-实际金额 + AddPositionVal decimal.Decimal `form:"addPositionVal"` //加仓/减仓 金额 RemainingQuantity decimal.Decimal `form:"remainingQuantity"` //剩余数量 TotalLossAmountU decimal.Decimal `form:"totalLossAmountU"` //累计亏损金额 } diff --git a/app/admin/service/dto/line_pre_order_ext.go b/app/admin/service/dto/line_pre_order_ext.go index 6b8e033..5d8c630 100644 --- a/app/admin/service/dto/line_pre_order_ext.go +++ b/app/admin/service/dto/line_pre_order_ext.go @@ -37,31 +37,32 @@ func (m *LinePreOrderExtGetPageReq) GetNeedSearch() interface{} { } type LineAddPreOrderExtReq struct { - TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比" ` - ReTakeProfitRatio decimal.Decimal `json:"reTakeProfitRatio" comment:"亏损回本止盈百分比"` - ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" comment:"减仓价格百分比" ` - ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" comment:"减仓数量百分比" ` - ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" comment:"减仓后止盈百分比" ` - ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" comment:"减仓后止损百分比"` - ReduceReTakeProfitRatio decimal.Decimal `json:"reduceReTakeProfitRatio" comment:"减仓后回本止盈百分比"` - AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" comment:"加仓价格百分比" ` - AddPositionOrderType string `json:"addPositionOrderType" comment:"加仓订单类型 LIMIT-限价 MARKET-市价"` - AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` - AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` + AddType int `json:"addType" comment:"类型 1-加仓 2-减仓"` + OrderType string `json:"orderType" comment:"订单类型 LIMIT-限价 MARKET-市价"` + PriceRatio decimal.Decimal `json:"priceRatio" comment:"价格百分比"` + AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` + AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` + TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` + StopLossRatio decimal.Decimal `json:"stopLossRatio" comment:"止损百分比"` + TakeProfitNumRatio decimal.Decimal `json:"takeProfitNumRatio" comment:"止盈数量百分比"` + TpTpPriceRatio decimal.Decimal `json:"tpTpPriceRatio" comment:"止盈后止盈价格百分比"` + TpSlPriceRatio decimal.Decimal `json:"tpSlPriceRatio" comment:"止盈后止损价格百分比"` + ReTakeProfitRatio decimal.Decimal `json:"reTakeProfitRatio" comment:"亏损回本止盈百分比"` } type LinePreOrderExtInsertReq struct { - Id int `json:"-" comment:"主键id"` // 主键id - MainOrderId int `json:"mainOrderId" comment:"主单id"` - OrderId int `json:"orderId" comment:"订单id"` - TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` - ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" comment:"减仓价格百分比"` - ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" comment:"减仓数量百分比"` - ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" comment:"减仓后止盈百分比"` - ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" comment:"减仓后止损百分比"` - AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" comment:"加仓价格百分比"` - AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` - AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` + Id int `json:"-" comment:"主键id"` // 主键id + MainOrderId int `json:"mainOrderId" comment:"主单id"` + OrderId int `json:"orderId" comment:"订单id"` + AddType int `json:"addType" comment:"类型 1-加仓 2-减仓"` + OrderType string `json:"orderType" comment:"类型 LIMIT-限价 MARKET-市价"` + PriceRatio decimal.Decimal `json:"priceRatio" comment:"价格百分比"` + TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` + TakeProfitNumRatio decimal.Decimal `json:"takeProfitNumRatio" comment:"止盈数量百分比"` + TpTpPriceRatio decimal.Decimal `json:"tpTpPriceRatio" comment:"止盈后止盈价格百分比"` + TpSlPriceRatio decimal.Decimal `json:"tpSlPriceRatio" comment:"止盈后止损价格百分比"` + AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` + AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` common.ControlBy } @@ -72,11 +73,11 @@ func (s *LinePreOrderExtInsertReq) Generate(model *models.LinePreOrderExt) { model.MainOrderId = s.MainOrderId model.OrderId = s.OrderId model.TakeProfitRatio = s.TakeProfitRatio - model.ReducePriceRatio = s.ReducePriceRatio - model.ReduceNumRatio = s.ReduceNumRatio - model.ReduceTakeProfitRatio = s.ReduceTakeProfitRatio - model.ReduceStopLossRatio = s.ReduceStopLossRatio - model.AddPositionPriceRatio = s.AddPositionPriceRatio + model.AddType = s.AddType + model.OrderType = s.OrderType + model.PriceRatio = s.PriceRatio + model.TpSlPriceRatio = s.TpSlPriceRatio + model.TpSlPriceRatio = s.TpSlPriceRatio model.AddPositionType = s.AddPositionType model.AddPositionVal = s.AddPositionVal model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 @@ -87,17 +88,18 @@ func (s *LinePreOrderExtInsertReq) GetId() interface{} { } type LinePreOrderExtUpdateReq struct { - Id int `uri:"id" comment:"主键id"` // 主键id - MainOrderId int `json:"mainOrderId" comment:"主单id"` - OrderId int `json:"orderId" comment:"订单id"` - TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` - ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" comment:"减仓价格百分比"` - ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" comment:"减仓数量百分比"` - ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" comment:"减仓后止盈百分比"` - ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" comment:"减仓后止损百分比"` - AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" comment:"加仓价格百分比"` - AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` - AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` + Id int `uri:"id" comment:"主键id"` // 主键id + MainOrderId int `json:"mainOrderId" comment:"主单id"` + OrderId int `json:"orderId" comment:"订单id"` + AddType int `json:"addType" comment:"类型 1-加仓 2-减仓"` + OrderType string `json:"orderType" comment:"类型 LIMIT-限价 MARKET-市价"` + PriceRatio decimal.Decimal `json:"priceRatio" comment:"价格百分比"` + TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` + TakeProfitNumRatio decimal.Decimal `json:"takeProfitNumRatio" comment:"止盈数量百分比"` + TpTpPriceRatio decimal.Decimal `json:"tpTpPriceRatio" comment:"止盈后止盈价格百分比"` + TpSlPriceRatio decimal.Decimal `json:"tpSlPriceRatio" comment:"止盈后止损价格百分比"` + AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` + AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` common.ControlBy } @@ -108,11 +110,11 @@ func (s *LinePreOrderExtUpdateReq) Generate(model *models.LinePreOrderExt) { model.MainOrderId = s.MainOrderId model.OrderId = s.OrderId model.TakeProfitRatio = s.TakeProfitRatio - model.ReducePriceRatio = s.ReducePriceRatio - model.ReduceNumRatio = s.ReduceNumRatio - model.ReduceTakeProfitRatio = s.ReduceTakeProfitRatio - model.ReduceStopLossRatio = s.ReduceStopLossRatio - model.AddPositionPriceRatio = s.AddPositionPriceRatio + model.AddType = s.AddType + model.OrderType = s.OrderType + model.PriceRatio = s.PriceRatio + model.TpSlPriceRatio = s.TpSlPriceRatio + model.TpSlPriceRatio = s.TpSlPriceRatio model.AddPositionType = s.AddPositionType model.AddPositionVal = s.AddPositionVal model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 0676804..78af479 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -11,6 +11,7 @@ import ( "go-admin/pkg/utility" "go-admin/pkg/utility/snowflakehelper" "go-admin/services/binanceservice" + "sort" "strconv" "strings" "time" @@ -338,6 +339,12 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP if req.SaveTemplate == "2" { return nil } + + //重新排序下跌比例(顺序) + sort.Slice(req.Ext, func(i, j int) bool { + return req.Ext[i].PriceRatio.Cmp(req.Ext[j].PriceRatio) < 0 + }) + var key string if req.SymbolType == global.SYMBOL_SPOT { key = fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, req.Symbol) @@ -356,11 +363,6 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //获取交易对 tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, key) - // orderCount := e.CheckRepeatOrder(req.SymbolType, id, req.Site, tradeSet.Coin) - // if orderCount > 0 { - // *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 该交易对已存在,请勿重复下单", id, req.Symbol)) - // continue - // } tickerPrice := utility.StrToDecimal(tradeSet.LastPrice) if tickerPrice.Equal(decimal.Zero) { //redis 没有这个值 *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 交易行情出错", id, req.Symbol)) @@ -454,15 +456,31 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //订单配置信息 preOrderExts := make([]models.LinePreOrderExt, 0) defultExt := models.LinePreOrderExt{ - TakeProfitRatio: utility.StringToDecimal(req.Profit), - ReducePriceRatio: req.ReducePriceRatio, - ReduceNumRatio: req.ReduceNumRatio, - ReduceTakeProfitRatio: req.ReduceTakeProfitRatio, - ReduceStopLossRatio: req.ReduceStopLossRatio, + AddType: 1, //主单等同于加仓0 + AddPositionType: 1, + AddPositionVal: decimal.Zero, + OrderType: req.PriceType, + TakeProfitRatio: utility.StringToDecimal(req.Profit), + TakeProfitNumRatio: req.ProfitNumRatio, + TpTpPriceRatio: req.ProfitTpTpPriceRatio, + TpSlPriceRatio: req.ProfitTpSlPriceRatio, + } + //减仓单 + defultExt2 := models.LinePreOrderExt{ + AddType: 2, + OrderType: "LIMIT", + PriceRatio: req.ReducePriceRatio, + AddPositionType: 1, + AddPositionVal: req.ReduceNumRatio, + TakeProfitRatio: req.ReduceTakeProfitRatio, + TakeProfitNumRatio: decimal.NewFromInt(100), //减仓止盈默认100% + StopLossRatio: req.ReduceStopLossRatio, } mainPrice := utility.StringToDecimal(AddOrder.Price) mainAmount := buyPrice.Div(mainPrice) - defultExt.TotalAfterReducing = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) + defultExt.TotalAfter = utility.StrToDecimal(AddOrder.Num).Truncate(int32(tradeSet.AmountDigit)) + defultExt2.TotalBefore = defultExt.TotalAfter + defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) preOrderExts = append(preOrderExts, defultExt) calculateResp := dto.CalculateBreakEvenRatioResp{} @@ -476,56 +494,42 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP LossEndPercent: req.ReducePriceRatio, AddPositionType: 2, AddPositionVal: decimal.Zero, - ReducePercent: req.ReduceNumRatio, } //计算减仓后 mainParam.LossEndPercent = req.ReducePriceRatio mainParam.RemainingQuantity = mainAmount - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) + e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet) mainParam.RemainingQuantity = calculateResp.RemainingQuantity //mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU //buyPrice.Mul(req.ReducePriceRatio.Div(decimal.NewFromInt(100)).Truncate(4)).Truncate(int32(tradeSet.PriceDigit)) req.ReduceReTakeProfitRatio = calculateResp.Ratio mainParam.LossBeginPercent = req.ReducePriceRatio - defultExt.ReduceReTakeRatio = calculateResp.Ratio + defultExt.ReTakeRatio = calculateResp.Ratio for index, addPosition := range req.Ext { ext := models.LinePreOrderExt{ - TakeProfitRatio: addPosition.TakeProfitRatio, - ReducePriceRatio: addPosition.ReducePriceRatio, - ReduceNumRatio: addPosition.ReduceNumRatio, - ReduceTakeProfitRatio: addPosition.ReduceTakeProfitRatio, - ReduceStopLossRatio: addPosition.ReduceStopLossRatio, - AddPositionPriceRatio: addPosition.AddPositionPriceRatio, - AddPositionOrderType: addPosition.AddPositionOrderType, - AddPositionType: addPosition.AddPositionType, - AddPositionVal: addPosition.AddPositionVal, + AddType: addPosition.AddType, + OrderType: addPosition.OrderType, + TakeProfitRatio: addPosition.TakeProfitRatio, + TakeProfitNumRatio: addPosition.TakeProfitNumRatio, + TpTpPriceRatio: addPosition.TpTpPriceRatio, + TpSlPriceRatio: addPosition.TpSlPriceRatio, + AddPositionType: addPosition.AddPositionType, + AddPositionVal: addPosition.AddPositionVal, } - mainParam.LossEndPercent = req.Ext[index].AddPositionPriceRatio + mainParam.LossEndPercent = req.Ext[index].PriceRatio mainParam.AddPositionType = req.Ext[index].AddPositionType mainParam.AddPositionVal = req.Ext[index].AddPositionVal - mainParam.ReducePercent = decimal.Zero - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) - ext.TotalAfterAdding = calculateResp.RemainingQuantity - req.Ext[index].ReTakeProfitRatio = calculateResp.Ratio - mainParam.LossBeginPercent = req.Ext[index].AddPositionPriceRatio + e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet) + + ext.TotalBefore = mainParam.RemainingQuantity //初始数量 + ext.TotalAfter = calculateResp.RemainingQuantity //计算后数量 + ext.ReTakeRatio = calculateResp.Ratio + mainParam.LossBeginPercent = addPosition.PriceRatio mainParam.RemainingQuantity = calculateResp.RemainingQuantity mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU - mainParam.LossEndPercent = req.Ext[index].ReducePriceRatio - mainParam.AddPositionVal = decimal.Zero - mainParam.ReducePercent = req.Ext[index].ReduceNumRatio - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) - - req.Ext[index].ReduceReTakeProfitRatio = calculateResp.Ratio - mainParam.LossBeginPercent = req.Ext[index].ReducePriceRatio - mainParam.RemainingQuantity = calculateResp.RemainingQuantity - mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU - - ext.TotalAfterReducing = calculateResp.RemainingQuantity - ext.ReTakeRatio = req.Ext[index].ReTakeProfitRatio - ext.ReduceReTakeRatio = req.Ext[index].ReduceReTakeProfitRatio preOrderExts = append(preOrderExts, ext) } @@ -576,7 +580,26 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP profitOrder.Rate = req.Profit profitOrder.MainId = AddOrder.Id + if req.ProfitNumRatio.Cmp(decimal.Zero) > 0 { + numPercent := req.ProfitNumRatio.Div(decimal.NewFromInt(100)) + profitOrder.Num = utility.StrToDecimal(profitOrder.Num).Mul(numPercent).Truncate(int32(tradeSet.AmountDigit)).String() + + } tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&profitOrder) + + //不全部止盈的时候 + if req.ProfitNumRatio.Cmp(decimal.Zero) > 0 && req.ProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { + reminQuantity := utility.StrToDecimal(AddOrder.Num).Sub(utility.StrToDecimal(profitOrder.Num)) + + childrens, err := makeTpOrder(&profitOrder, reminQuantity, req.ProfitTpTpPriceRatio, req.ProfitTpSlPriceRatio, &tradeSet) + + if err != nil { + logger.Error("生成止盈后子订单失败") + return err + } + + tx.Create(&childrens) + } } if req.ReducePriceRatio.Cmp(decimal.Zero) > 0 { @@ -619,39 +642,44 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP preOrderExts[index].OrderId = AddOrder.Id continue } + var AddOrder models.LinePreOrder - addPosition := createPreAddPosition(&AddOrder, v, tradeSet) + if v.AddType == 1 { + AddOrder = createPreAddPosition(&AddOrder, v, tradeSet) + } else if v.AddType == 2 { + AddOrder = createPreReduceOrder(&AddOrder, v, tradeSet) + } - if addPosition.OrderSn == "" { + if AddOrder.OrderSn == "" { continue } - if err := e.Orm.Create(&addPosition).Error; err != nil { + if err := e.Orm.Create(&AddOrder).Error; err != nil { logger.Error("保存加仓单失败") return err } - preOrderExts[index].OrderId = addPosition.Id + preOrderExts[index].OrderId = AddOrder.Id //止盈、减仓 - orders, err := makeFuturesTakeAndReduce(&addPosition, v, tradeSet) + orders, err := makeFuturesTakeAndReduce(&AddOrder, v, tradeSet) if err != nil { - logger.Error("构造加仓单止盈、减仓失败") + logger.Error("构造止盈、止损失败") return err } if err := e.Orm.Create(&orders).Error; err != nil { - logger.Error("保存加仓单止盈、减仓失败") + logger.Error("保存止盈、止损失败") return err } for index := range orders { //减仓单且 减仓比例大于0 小于100 就冲下止盈止损 - if orders[index].OrderType == 4 && v.ReduceNumRatio.Cmp(decimal.Zero) > 0 && v.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { + if orders[index].OrderType == 1 && v.TakeProfitRatio.Cmp(decimal.Zero) > 0 && v.TakeProfitRatio.Cmp(decimal.NewFromInt(100)) < 0 { reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet) if err != nil { - logger.Error("生产加仓单止盈、减仓失败") + logger.Error("生产止盈后止盈、减仓失败") return err } @@ -660,7 +688,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP } if err := e.Orm.Create(&reduceChildOrders).Error; err != nil { - logger.Error("报错减仓后止盈止损失败") + logger.Error("报错止盈后止盈止损失败") return err } } @@ -695,16 +723,16 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx data.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) data.MainId = preOrder.Id data.CreatedAt = time.Now() - data.MainOrderType = v.AddPositionOrderType + data.MainOrderType = v.OrderType data.Status = 0 data.OrderCategory = 3 - data.Rate = v.AddPositionPriceRatio.String() + data.Rate = v.PriceRatio.String() var percentage decimal.Decimal if data.Site == "BUY" { - percentage = decimal.NewFromInt(1).Sub(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) + percentage = decimal.NewFromInt(1).Sub(v.PriceRatio.Div(decimal.NewFromInt(100))) } else { - percentage = decimal.NewFromInt(1).Add(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) + percentage = decimal.NewFromInt(1).Add(v.PriceRatio.Div(decimal.NewFromInt(100))) } dataPrice := price.Mul(percentage).Truncate(int32(tradeSet.PriceDigit)) @@ -722,39 +750,12 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx return data } -// 构建合约止盈、减仓单 -func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { - num := ext.TotalAfterAdding.Truncate(int32(tradeSet.AmountDigit)) - orders := make([]models.LinePreOrder, 0) - //止盈单 - profitOrder := models.LinePreOrder{} - copier.Copy(&profitOrder, preOrder) - - profitOrder.Id = 0 - profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) - profitOrder.Pid = preOrder.Id - profitOrder.OrderType = 1 - profitOrder.Status = 0 - profitOrder.MainId = preOrder.MainId - profitOrder.Num = num.String() - profitOrder.BuyPrice = "0" - // profitOrder.Rate = ext.TakeProfitRatio.String() - - //止盈需要累加之前的亏损 - profitOrder.Rate = ext.TakeProfitRatio.Add(ext.ReTakeRatio).Truncate(2).String() - - if strings.ToUpper(preOrder.Site) == "BUY" { - profitOrder.Site = "SELL" - } else { - profitOrder.Site = "BUY" - } - - binanceservice.SetPrice(&profitOrder, preOrder, tradeSet) - orders = append(orders, profitOrder) +// 生成减仓单 +func createPreReduceOrder(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) models.LinePreOrder { + var stopOrder models.LinePreOrder //减仓单 - if ext.ReducePriceRatio.Cmp(decimal.Zero) > 0 { - stopOrder := models.LinePreOrder{} + if ext.PriceRatio.Cmp(decimal.Zero) > 0 { copier.Copy(&stopOrder, preOrder) stopOrder.Id = 0 @@ -763,14 +764,10 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO stopOrder.MainId = preOrder.MainId stopOrder.OrderType = 4 stopOrder.Status = 0 - stopOrder.Rate = ext.ReducePriceRatio.String() - stopOrder.Num = num.String() + stopOrder.Rate = ext.PriceRatio.String() + stopOrder.Num = ext.TotalAfter.Sub(ext.TotalBefore).Abs().Truncate(int32(tradeSet.AmountDigit)).String() stopOrder.BuyPrice = "0" - if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { - stopNum := num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))) - stopOrder.Num = stopNum.Truncate(int32(tradeSet.AmountDigit)).String() - } if strings.ToUpper(preOrder.Site) == "BUY" { stopOrder.Site = "SELL" } else { @@ -778,38 +775,140 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO } binanceservice.SetPrice(&stopOrder, preOrder, tradeSet) - orders = append(orders, stopOrder) + } + + return stopOrder +} + +// 构建止盈、止盈止损 +func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { + orders := make([]models.LinePreOrder, 0) + var side string + + if strings.ToUpper(preOrder.Site) == "BUY" { + side = "SELL" + } else { + side = "BUY" + } + + if ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 { + // 止盈单 + profitOrder := models.LinePreOrder{} + copier.Copy(&profitOrder, preOrder) + + profitOrder.Id = 0 + profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) + profitOrder.Pid = preOrder.Id + profitOrder.OrderType = 1 + profitOrder.Status = 0 + profitOrder.MainId = preOrder.MainId + profitOrder.BuyPrice = "0" + profitOrder.Site = side + + if ext.TakeProfitNumRatio.Cmp(decimal.Zero) <= 0 || ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) >= 0 { + profitOrder.Num = ext.TotalAfter.Truncate(int32(tradeSet.AmountDigit)).String() + } else { + profitOrder.Num = ext.TotalAfter.Mul(ext.TakeProfitNumRatio).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)).String() + } + + // 止盈需要累加之前的亏损 + profitOrder.Rate = ext.TakeProfitRatio.Add(ext.ReTakeRatio).Truncate(2).String() + + binanceservice.SetPrice(&profitOrder, preOrder, tradeSet) + orders = append(orders, profitOrder) + } + + if ext.StopLossRatio.Cmp(decimal.Zero) > 0 { + lossOrder := models.LinePreOrder{} + copier.Copy(&lossOrder, preOrder) + + lossOrder.Id = 0 + lossOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) + lossOrder.Pid = preOrder.Id + lossOrder.OrderType = 2 + lossOrder.Status = 0 + lossOrder.MainId = preOrder.MainId + lossOrder.BuyPrice = "0" + lossOrder.Num = ext.TotalAfter.Truncate(int32(tradeSet.AmountDigit)).String() + lossOrder.Rate = ext.StopLossRatio.Truncate(2).String() + lossOrder.Site = side + + binanceservice.SetPrice(&lossOrder, preOrder, tradeSet) + orders = append(orders, lossOrder) } return orders, nil } +// 构建止盈后止盈止损 +// parentOrder 父订单 +// remainQuantity 剩余数量 +// tpPriceRatio 止盈价格比例 +// slPriceRatio 止损价格比例 +func makeTpOrder(parentOrder *models.LinePreOrder, reminQuantity decimal.Decimal, tpPriceRatio, slPriceRatio decimal.Decimal, tradeSet *models2.TradeSet) ([]models.LinePreOrder, error) { + result := make([]models.LinePreOrder, 0) + tp := models.LinePreOrder{} + sl := models.LinePreOrder{} + copier.Copy(&tp, parentOrder) + + tp.Id = 0 + tp.Pid = parentOrder.Id + tp.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) + tp.OrderType = 1 + tp.Status = 0 + tp.Rate = tpPriceRatio.String() + tp.Num = reminQuantity.String() + binanceservice.SetPrice(&tp, parentOrder, *tradeSet) + result = append(result, tp) + + if (slPriceRatio).Cmp(decimal.Zero) > 0 { + copier.Copy(&sl, parentOrder) + sl.Pid = parentOrder.Id + sl.Id = 0 + sl.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) + sl.OrderType = 2 + sl.Num = reminQuantity.Truncate(int32(tradeSet.AmountDigit)).String() + sl.Rate = slPriceRatio.String() + binanceservice.SetPrice(&sl, parentOrder, *tradeSet) + result = append(result, sl) + } + + return result, nil +} + // 构建减仓后止盈止损 func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { orders := make([]models.LinePreOrder, 0) - takeProfitOrder := models.LinePreOrder{} - copier.Copy(&takeProfitOrder, parentOrder) - takeProfitOrder.Id = 0 - takeProfitOrder.Pid = parentOrder.Id - takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) - takeProfitOrder.Status = 0 - takeProfitOrder.OrderType = 1 - takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String() - takeProfitOrder.SignPrice = parentOrder.Price - takeProfitOrder.CreatedAt = time.Now() - takeProfitOrder.BuyPrice = "0" - takeProfitOrder.MainOrderType = "LIMIT" - takeProfitOrder.Num = ext.TotalAfterReducing.Truncate(int32(tradeSet.AmountDigit)).String() - // takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String() - //止盈需要累加之前的亏损 - takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.Add(ext.ReduceReTakeRatio).String() - takeProfitOrder.BuyPrice = "0" + var num decimal.Decimal - binanceservice.SetPrice(&takeProfitOrder, parentOrder, tradeSet) - orders = append(orders, takeProfitOrder) + if ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { + percent := decimal.NewFromInt(1).Sub(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))) + num = ext.TotalAfter.Mul(percent).Truncate(int32(tradeSet.AmountDigit)) + } + if ext.TpTpPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { + takeProfitOrder := models.LinePreOrder{} + copier.Copy(&takeProfitOrder, parentOrder) + takeProfitOrder.Id = 0 + takeProfitOrder.Pid = parentOrder.Id + takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) + takeProfitOrder.Status = 0 + takeProfitOrder.OrderType = 1 + takeProfitOrder.Rate = ext.TpTpPriceRatio.String() + takeProfitOrder.SignPrice = parentOrder.Price + takeProfitOrder.CreatedAt = time.Now() + takeProfitOrder.BuyPrice = "0" + takeProfitOrder.MainOrderType = "LIMIT" + takeProfitOrder.Num = num.String() + //止盈需要累加之前的亏损 + takeProfitOrder.Rate = ext.TpTpPriceRatio.Truncate(2).String() + takeProfitOrder.BuyPrice = "0" + + binanceservice.SetPrice(&takeProfitOrder, parentOrder, tradeSet) + orders = append(orders, takeProfitOrder) + } //有止损单 - if ext.ReduceStopLossRatio.Cmp(decimal.Zero) > 0 { + if ext.TpSlPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { var stoploss models.LinePreOrder copier.Copy(&stoploss, parentOrder) @@ -821,9 +920,9 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line stoploss.OrderType = 2 stoploss.SignPrice = parentOrder.Price stoploss.BuyPrice = "0" - stoploss.Rate = ext.ReduceStopLossRatio.String() + stoploss.Rate = ext.TpSlPriceRatio.String() stoploss.MainOrderType = "LIMIT" - stoploss.Num = ext.TotalAfterReducing.Truncate(int32(tradeSet.AmountDigit)).String() + stoploss.Num = num.String() stoploss.BuyPrice = "0" binanceservice.SetPrice(&stoploss, parentOrder, tradeSet) @@ -1633,7 +1732,7 @@ func (e *LinePreOrder) QueryAiCoinPrice(req *dto.QueryAiCoinPriceReq) (models.Li } // 根据请求参数重新生成亏损回本止盈百分比 -func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) ([]models.LineDirection, error) { +func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) error { var tradeSet models2.TradeSet var tickerPrice decimal.Decimal @@ -1644,7 +1743,7 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) ([]models.Line } if tradeSet.LastPrice == "" { - return nil, errors.New("获取不到交易对信息") + return errors.New("获取不到交易对信息") } var price decimal.Decimal @@ -1666,77 +1765,58 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) ([]models.Line buyPrice := utility.StrToDecimal(req.BuyPrice) mainAmount := buyPrice.Div(price).Truncate(int32(tradeSet.AmountDigit)) calculateResp := dto.CalculateBreakEvenRatioResp{} + lossBeginPercent := decimal.Zero mainParam := dto.CalculateBreakEevenRatioReq{ Price: price, ExchangeType: req.ExchangeType, Symbol: req.Symbol, SymbolType: req.SymbolType, BuyPrice: buyPrice, - LossBeginPercent: decimal.Zero, + LossBeginPercent: lossBeginPercent, LossEndPercent: req.ReducePriceRatio, AddPositionType: 2, AddPositionVal: decimal.Zero, - ReducePercent: req.ReduceNumRatio, } //计算减仓后 mainParam.LossEndPercent = req.ReducePriceRatio mainParam.RemainingQuantity = mainAmount - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) + mainParam.AddType = 2 + e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet) mainParam.RemainingQuantity = calculateResp.RemainingQuantity mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU req.ReduceReTakeProfitRatio = calculateResp.Ratio - mainParam.LossBeginPercent = req.ReducePriceRatio + lossBeginPercent = req.ReducePriceRatio + + //顺序排序 + sort.Slice(req.Ext, func(i, j int) bool { + return req.Ext[i].PriceRatio.Cmp(req.Ext[j].PriceRatio) < 0 + }) for index := range req.Ext { - mainParam.LossEndPercent = req.Ext[index].AddPositionPriceRatio + mainParam.LossBeginPercent = lossBeginPercent + mainParam.LossEndPercent = req.Ext[index].PriceRatio mainParam.AddPositionType = req.Ext[index].AddPositionType mainParam.AddPositionVal = req.Ext[index].AddPositionVal - mainParam.ReducePercent = decimal.Zero - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) + + e.CalculateBreakEvenRatio(&mainParam, &calculateResp, tradeSet) req.Ext[index].ReTakeProfitRatio = calculateResp.Ratio - mainParam.LossBeginPercent = req.Ext[index].AddPositionPriceRatio - mainParam.RemainingQuantity = calculateResp.RemainingQuantity - mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU - mainParam.LossEndPercent = req.Ext[index].ReducePriceRatio - mainParam.AddPositionVal = decimal.Zero - mainParam.ReducePercent = req.Ext[index].ReduceNumRatio - e.CalculateBreakEvenRatio(&mainParam, &calculateResp) - - req.Ext[index].ReduceReTakeProfitRatio = calculateResp.Ratio - mainParam.LossBeginPercent = req.Ext[index].ReducePriceRatio + lossBeginPercent = req.Ext[index].PriceRatio mainParam.RemainingQuantity = calculateResp.RemainingQuantity mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU } - return nil, nil + return nil } // 计算亏损百分比 -func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatioReq, data *dto.CalculateBreakEvenRatioResp) error { - var tradeSet models2.TradeSet +func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatioReq, data *dto.CalculateBreakEvenRatioResp, tradeSet models2.TradeSet) error { if req.LossEndPercent.Cmp(req.LossBeginPercent) < 0 { return errors.New("截至亏损百分比必须大于开始亏损百分比") } - if req.SymbolType == 1 { - val, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, req.Symbol)) - - sonic.Unmarshal([]byte(val), &tradeSet) - } else if req.SymbolType == 2 { - val, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_FUTURES, req.ExchangeType, req.Symbol)) - - sonic.Unmarshal([]byte(val), &tradeSet) - } else { - return errors.New("symbolType error") - } - - if tradeSet.LastPrice == "" { - return errors.New("没有找到交易对行情") - } - var addPositionBuyPrice decimal.Decimal if req.AddPositionType == 1 { @@ -1759,8 +1839,8 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100).Truncate(4))).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.PriceDigit)) //计算减仓数量 - if req.ReducePercent.Cmp(decimal.NewFromInt(0)) > 0 { - reduceAmount = totalAmount.Mul(req.ReducePercent.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.AmountDigit)) + if req.AddPositionType == 2 && req.AddPositionVal.Cmp(decimal.NewFromInt(0)) > 0 { + reduceAmount = totalAmount.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.AmountDigit)) } data.RemainingQuantity = totalAmount.Sub(reduceAmount) @@ -1776,163 +1856,163 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio return nil } -// 手动加仓 -func (e *LinePreOrder) AddPosition(req *dto.LinePreOrderAddPositionReq) error { - lastPositionOrder := models.LinePreOrder{} - var tradeSet models2.TradeSet +// // 手动加仓 +// func (e *LinePreOrder) AddPosition(req *dto.LinePreOrderAddPositionReq) error { +// lastPositionOrder := models.LinePreOrder{} +// var tradeSet models2.TradeSet - if req.OrderType == 1 { - tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 0) - } else if req.OrderType == 2 { - tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 1) - } else { - return fmt.Errorf("交易对:%s 订单类型错误", req.Symbol) - } +// if req.OrderType == 1 { +// tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 0) +// } else if req.OrderType == 2 { +// tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 1) +// } else { +// return fmt.Errorf("交易对:%s 订单类型错误", req.Symbol) +// } - if tradeSet.LastPrice == "" { - return fmt.Errorf("交易对:%s 交易对配置错误", req.Symbol) - } +// if tradeSet.LastPrice == "" { +// return fmt.Errorf("交易对:%s 交易对配置错误", req.Symbol) +// } - if err := e.Orm.Model(&lastPositionOrder).Where("symbol =? AND status = 6 AND site =? AND api_id =? AND symbol_type =? AND exchange_type=?", - req.Symbol, req.Site, req.ApiUserId, req.OrderType, req.ExchangeType).Error; err != nil { - logger.Errorf("交易对:%s查询已开仓订单失败", req.Symbol) - return fmt.Errorf("交易对:%s 没有已开仓的订单", req.Symbol) - } +// if err := e.Orm.Model(&lastPositionOrder).Where("symbol =? AND status = 6 AND site =? AND api_id =? AND symbol_type =? AND exchange_type=?", +// req.Symbol, req.Site, req.ApiUserId, req.OrderType, req.ExchangeType).Error; err != nil { +// logger.Errorf("交易对:%s查询已开仓订单失败", req.Symbol) +// return fmt.Errorf("交易对:%s 没有已开仓的订单", req.Symbol) +// } - ext := models.LinePreOrderExt{ - MainOrderId: lastPositionOrder.Id, - TakeProfitRatio: req.Profit, - ReducePriceRatio: req.ReducePriceRatio, - ReduceNumRatio: req.ReduceNumRatio, - ReduceTakeProfitRatio: req.ReduceTakeProfitRatio, - ReduceStopLossRatio: req.ReduceStopLossRatio, - AddPositionOrderType: req.AddPositionOrderType, - AddPositionType: 2, - AddPositionVal: req.BuyPrice, - } +// ext := models.LinePreOrderExt{ +// MainOrderId: lastPositionOrder.Id, +// TakeProfitRatio: req.Profit, +// ReducePriceRatio: req.ReducePriceRatio, +// ReduceNumRatio: req.ReduceNumRatio, +// ReduceTakeProfitRatio: req.ReduceTakeProfitRatio, +// ReduceStopLossRatio: req.ReduceStopLossRatio, +// AddPositionOrderType: req.AddPositionOrderType, +// AddPositionType: 2, +// AddPositionVal: req.BuyPrice, +// } - addPosition := models.LinePreOrder{ - SignPrice: tradeSet.LastPrice, - Pid: lastPositionOrder.Id, - MainId: lastPositionOrder.Id, - Symbol: req.Symbol, - QuoteSymbol: tradeSet.Currency, - SignPriceU: utility.StrToDecimal(tradeSet.LastPrice), - ApiId: req.ApiUserId, - Site: req.Site, - ExchangeType: req.ExchangeType, - OrderType: 0, - OrderCategory: 3, - BuyPrice: req.BuyPrice.String(), - Status: 0, - SymbolType: req.OrderType, - MainOrderType: req.AddPositionOrderType, - ExpireTime: time.Now().Add(4), - OrderSn: utility.Int64ToString(snowflakehelper.GetOrderId()), - } +// addPosition := models.LinePreOrder{ +// SignPrice: tradeSet.LastPrice, +// Pid: lastPositionOrder.Id, +// MainId: lastPositionOrder.Id, +// Symbol: req.Symbol, +// QuoteSymbol: tradeSet.Currency, +// SignPriceU: utility.StrToDecimal(tradeSet.LastPrice), +// ApiId: req.ApiUserId, +// Site: req.Site, +// ExchangeType: req.ExchangeType, +// OrderType: 0, +// OrderCategory: 3, +// BuyPrice: req.BuyPrice.String(), +// Status: 0, +// SymbolType: req.OrderType, +// MainOrderType: req.AddPositionOrderType, +// ExpireTime: time.Now().Add(4), +// OrderSn: utility.Int64ToString(snowflakehelper.GetOrderId()), +// } - tickerPrice := utility.StrToDecimal(tradeSet.LastPrice) - if req.PricePattern == "percentage" { - addPosition.Rate = req.Price.String() - priceRate := req.Price.Div(decimal.NewFromInt(100)) //下单价除100 =0.1 +// tickerPrice := utility.StrToDecimal(tradeSet.LastPrice) +// if req.PricePattern == "percentage" { +// addPosition.Rate = req.Price.String() +// priceRate := req.Price.Div(decimal.NewFromInt(100)) //下单价除100 =0.1 - if strings.ToUpper(req.Site) == "BUY" { //购买方向 - //实际下单价格 - truncate := tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit)) - addPosition.Price = truncate.String() - } else { - truncate := tickerPrice.Mul(decimal.NewFromInt(1).Add(priceRate)).Truncate(int32(tradeSet.PriceDigit)) - addPosition.Price = truncate.String() - } +// if strings.ToUpper(req.Site) == "BUY" { //购买方向 +// //实际下单价格 +// truncate := tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit)) +// addPosition.Price = truncate.String() +// } else { +// truncate := tickerPrice.Mul(decimal.NewFromInt(1).Add(priceRate)).Truncate(int32(tradeSet.PriceDigit)) +// addPosition.Price = truncate.String() +// } - } else { //实际价格下单 - addPosition.Price = req.Price.Truncate(int32(tradeSet.PriceDigit)).String() - addPosition.SignPriceType = req.PricePattern - addPosition.Rate = "0" - } - if tradeSet.Currency != "USDT" { //不是U本位 - //获取币本位兑换u的价格 - ticker2 := models2.TradeSet{} - tickerVal, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, strings.ToUpper(tradeSet.Coin+"USDT"))) +// } else { //实际价格下单 +// addPosition.Price = req.Price.Truncate(int32(tradeSet.PriceDigit)).String() +// addPosition.SignPriceType = req.PricePattern +// addPosition.Rate = "0" +// } +// if tradeSet.Currency != "USDT" { //不是U本位 +// //获取币本位兑换u的价格 +// ticker2 := models2.TradeSet{} +// tickerVal, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, strings.ToUpper(tradeSet.Coin+"USDT"))) - if tickerVal == "" { - logger.Error("查询行情失败") - return fmt.Errorf("交易对:%s 获取u本位行情失败", req.Symbol) - } +// if tickerVal == "" { +// logger.Error("查询行情失败") +// return fmt.Errorf("交易对:%s 获取u本位行情失败", req.Symbol) +// } - err := sonic.Unmarshal([]byte(tickerVal), &ticker2) +// err := sonic.Unmarshal([]byte(tickerVal), &ticker2) - if ticker2.LastPrice == "" { - logger.Errorf("查询行情失败 %s err:%v", strings.ToUpper(tradeSet.Coin+"USDT"), err) - return fmt.Errorf("交易对:%s 获取u本位行情 反序列化失败", req.Symbol) - } - //LTCBTC --> LTCUSDT - uTickerPrice := utility.StrToDecimal(ticker2.LastPrice) //94069 - //换算成U - //div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365 - //在换算成对应交易对对应的价值 - //LTCBTC --> LTCUSDT == LTCUSDT -- 100.502 - div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice)) - //计算下单数量 - addPosition.Num = req.BuyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String() - } else { - fromString, _ := decimal.NewFromString(addPosition.Price) - addPosition.Num = req.BuyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String() - } - //事务保存 - err := e.Orm.Transaction(func(tx *gorm.DB) error { - //添加加仓单 - if err := tx.Create(&addPosition).Error; err != nil { - return err - } +// if ticker2.LastPrice == "" { +// logger.Errorf("查询行情失败 %s err:%v", strings.ToUpper(tradeSet.Coin+"USDT"), err) +// return fmt.Errorf("交易对:%s 获取u本位行情 反序列化失败", req.Symbol) +// } +// //LTCBTC --> LTCUSDT +// uTickerPrice := utility.StrToDecimal(ticker2.LastPrice) //94069 +// //换算成U +// //div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365 +// //在换算成对应交易对对应的价值 +// //LTCBTC --> LTCUSDT == LTCUSDT -- 100.502 +// div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice)) +// //计算下单数量 +// addPosition.Num = req.BuyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String() +// } else { +// fromString, _ := decimal.NewFromString(addPosition.Price) +// addPosition.Num = req.BuyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String() +// } +// //事务保存 +// err := e.Orm.Transaction(func(tx *gorm.DB) error { +// //添加加仓单 +// if err := tx.Create(&addPosition).Error; err != nil { +// return err +// } - //止盈、减仓 - orders, err := makeFuturesTakeAndReduce(&addPosition, ext, tradeSet) +// //止盈、减仓 +// orders, err := makeFuturesTakeAndReduce(&addPosition, ext, tradeSet) - if err != nil { - logger.Error("构造加仓单止盈、减仓失败") - return err - } +// if err != nil { +// logger.Error("构造加仓单止盈、减仓失败") +// return err +// } - if err := e.Orm.Create(&orders).Error; err != nil { - logger.Error("保存加仓单止盈、减仓失败") - return err - } +// if err := e.Orm.Create(&orders).Error; err != nil { +// logger.Error("保存加仓单止盈、减仓失败") +// return err +// } - //处理减仓单 - for index := range orders { - //减仓单且 减仓比例大于0 小于100 就冲下止盈止损 - if orders[index].OrderType == 4 && ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 && ext.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { - reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), ext, tradeSet) +// //处理减仓单 +// for index := range orders { +// //减仓单且 减仓比例大于0 小于100 就冲下止盈止损 +// if orders[index].OrderType == 4 && ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 && ext.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { +// reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), ext, tradeSet) - if err != nil { - logger.Error("生产加仓单止盈、减仓失败") - return err - } +// if err != nil { +// logger.Error("生产加仓单止盈、减仓失败") +// return err +// } - if len(reduceChildOrders) == 0 { - continue - } +// if len(reduceChildOrders) == 0 { +// continue +// } - if err := e.Orm.Create(&reduceChildOrders).Error; err != nil { - logger.Error("报错减仓后止盈止损失败") - return err - } - } - } +// if err := e.Orm.Create(&reduceChildOrders).Error; err != nil { +// logger.Error("报错减仓后止盈止损失败") +// return err +// } +// } +// } - ext.OrderId = addPosition.Id - if err := tx.Create(&ext).Error; err != nil { - return err - } +// ext.OrderId = addPosition.Id +// if err := tx.Create(&ext).Error; err != nil { +// return err +// } - return nil - }) +// return nil +// }) - if err != nil { - logger.Errorf("交易对:%s 添加加仓订单失败", req.Symbol) - return fmt.Errorf("交易对:%s 添加加仓订单失败", req.Symbol) - } +// if err != nil { +// logger.Errorf("交易对:%s 添加加仓订单失败", req.Symbol) +// return fmt.Errorf("交易对:%s 添加加仓订单失败", req.Symbol) +// } - return nil -} +// return nil +// } diff --git a/app/admin/service/line_symbol.go b/app/admin/service/line_symbol.go index 9a8f2d7..362e65f 100644 --- a/app/admin/service/line_symbol.go +++ b/app/admin/service/line_symbol.go @@ -294,17 +294,14 @@ func (e *LineSymbol) ResetSpotSymbol() error { logger.Error("获取币安现货交易对失败") return err } - symbols := make([]models.LineSymbol, 0) - + insertSymbols := make([]models.LineSymbol, 0) sysConfig := SysConfig{Service: service.Service{Orm: e.Orm}} var req = new(dto.SysConfigByKeyReq) var resp = new(dto.GetSysConfigByKEYForServiceResp) req.ConfigKey = "quote_volume_24hr" sysConfig.GetWithKey(req, resp) - symbolBlack := make([]models.LineSymbolBlack, 0) - e.Orm.Model(&models.LineSymbolBlack{}).Where("type = '1'").Find(&symbolBlack) - + symbolBlackMap := getSymbolBlackMap(e, "1") type Ticker struct { Symbol string `json:"symbol"` Price string `json:"price"` @@ -317,17 +314,16 @@ func (e *LineSymbol) ResetSpotSymbol() error { return err } + //判断是否在黑名单、是否需要修改 for symbol, tradeSet := range tradeSets { key := fmt.Sprintf(global.TICKER_SPOT, global.EXCHANGE_BINANCE, symbol) //判断是否在黑名单里面 - for _, black := range symbolBlack { - if black.Symbol == symbol { - helper.DefaultRedis.DeleteString(key) - deleteSymbols = append(deleteSymbols, symbol) - continue - } + if _, ok := symbolBlackMap[symbol]; ok { + helper.DefaultRedis.DeleteString(key) + deleteSymbols = append(deleteSymbols, symbol) + continue } val := helper.DefaultRedis.Get(key).Val() @@ -369,7 +365,14 @@ func (e *LineSymbol) ResetSpotSymbol() error { if lineSymbol.Symbol == "" { continue } - symbols = append(symbols, lineSymbol) + insertSymbols = append(insertSymbols, lineSymbol) + } + } + + //判断已经移除的交易对 + for _, v := range oldMapSymbols { + if _, ok := tradeSets[v.Symbol]; !ok && !utility.ContainsStr(deleteSymbols, v.Symbol) { + deleteSymbols = append(deleteSymbols, v.Symbol) } } @@ -393,8 +396,8 @@ func (e *LineSymbol) ResetSpotSymbol() error { batchDeleteBySymbols(deleteSymbols, "1", e) } - if len(symbols) > 0 { - err := e.Orm.Model(&models.LineSymbol{}).Omit("api_id").Create(&symbols).Error + if len(insertSymbols) > 0 { + err := e.Orm.Model(&models.LineSymbol{}).Omit("api_id").Create(&insertSymbols).Error if err != nil { return err } @@ -403,6 +406,18 @@ func (e *LineSymbol) ResetSpotSymbol() error { return nil } +func getSymbolBlackMap(e *LineSymbol, symbolType string) map[string]models.LineSymbolBlack { + symbolBlack := make([]models.LineSymbolBlack, 0) + symbolBlackMap := make(map[string]models.LineSymbolBlack) + + e.Orm.Model(&models.LineSymbolBlack{}).Where("type = ?", symbolType).Find(&symbolBlack) + + for _, v := range symbolBlack { + symbolBlackMap[v.Symbol] = v + } + return symbolBlackMap +} + // 批量删除 根据交易对名 // symbolType 1-现货 2-合约 func batchDeleteBySymbols(deleteSymbols []string, symbolType string, e *LineSymbol) { @@ -459,8 +474,7 @@ func (e *LineSymbol) ResetFuturesSymbol() error { req.ConfigKey = "quote_volume_24hr" sysConfig.GetWithKey(req, resp) symbols := make([]models.LineSymbol, 0) - symbolBlack := make([]models.LineSymbolBlack, 0) - + symbolBlackMap := getSymbolBlackMap(e, "2") type Ticker struct { Symbol string `json:"symbol"` Price string `json:"price"` @@ -473,19 +487,17 @@ func (e *LineSymbol) ResetFuturesSymbol() error { return err } - e.Orm.Model(&models.LineSymbolBlack{}).Where("type = 2").Find(&symbolBlack) + //判断是否在黑名单、是否需要新增 for symbol, tradeSet := range tradeSets { - key := fmt.Sprintf(global.TICKER_FUTURES, global.EXCHANGE_BINANCE, symbol) //判断是否在黑名单里面 - for _, black := range symbolBlack { - if black.Symbol == symbol { - helper.DefaultRedis.DeleteString(key) - deleteSymbols = append(deleteSymbols, symbol) - continue - } + if _, ok := symbolBlackMap[symbol]; ok { + helper.DefaultRedis.DeleteString(key) + deleteSymbols = append(deleteSymbols, symbol) + continue } + val := helper.DefaultRedis.Get(key).Val() var spotTicker24h commonModels.TradeSet sonic.Unmarshal([]byte(val), &spotTicker24h) @@ -528,6 +540,13 @@ func (e *LineSymbol) ResetFuturesSymbol() error { } } + //交易所已经没有的交易对直接去除 + for _, v := range oldMapSymbols { + if _, ok := tradeSets[v.Symbol]; !ok && !utility.ContainsStr(deleteSymbols, v.Symbol) { + deleteSymbols = append(deleteSymbols, v.Symbol) + } + } + groups, err := getSymbolGroups(e, "2") if err != nil { return err diff --git a/app/admin/service/line_symbol_black.go b/app/admin/service/line_symbol_black.go index 4c1e7f9..b65ec9e 100644 --- a/app/admin/service/line_symbol_black.go +++ b/app/admin/service/line_symbol_black.go @@ -79,15 +79,11 @@ func (e *LineSymbolBlack) Insert(c *dto.LineSymbolBlackInsertReq) error { } err = e.Orm.Create(&data).Error - if err != nil { - e.Log.Errorf("LineSymbolBlackService Insert error:%s \r\n", err) - return err - } - return e.reloadSymbol(c.Type) + return err } -func (e *LineSymbolBlack) reloadSymbol(symbolType string) error { +func (e *LineSymbolBlack) ReloadSymbol(symbolType string) error { symbolService := LineSymbol{Service: e.Service} if symbolType == "1" { @@ -133,21 +129,12 @@ func (e *LineSymbolBlack) Update(c *dto.LineSymbolBlackUpdateReq, p *actions.Dat if db.RowsAffected == 0 { return errors.New("无权更新该数据") } - return e.reloadSymbol(c.Type) + return nil } // Remove 删除LineSymbolBlack func (e *LineSymbolBlack) Remove(d *dto.LineSymbolBlackDeleteReq, p *actions.DataPermission) error { var data models.LineSymbolBlack - types := make([]string, 0) - - e.Orm.Model(&data).Where("id in ?", d.GetId()).Select("type").Distinct().Find(&types) - - for _, v := range types { - if v != "" { - e.reloadSymbol(v) - } - } db := e.Orm.Model(&data). Scopes( diff --git a/services/binanceservice/commonservice.go b/services/binanceservice/commonservice.go index 091f1c6..adcd648 100644 --- a/services/binanceservice/commonservice.go +++ b/services/binanceservice/commonservice.go @@ -461,7 +461,7 @@ func getOpenPositionMainOrderId(db *gorm.DB, newId, apiId, symbolType int, excha mainOrders := make([]DbModels.LinePreOrder, 0) if err := db.Model(&DbModels.LinePreOrder{}). - Where("api_id =? AND status>4 AND status<7 AND symbol=? AND symbol_type =? AND site= ? AND exchange_type=? AND id!=?", + Where("api_id =? AND status>4 AND order_type =0 AND status<7 AND symbol=? AND symbol_type =? AND site= ? AND exchange_type=? AND id!=?", apiId, symbol, symbolType, side, exchangeType, newId). Select("id", "main_id", "order_sn").Find(&mainOrders).Error; err != nil { return nil, err diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 8c32a08..6f075ac 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -93,7 +93,7 @@ func handleFutOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderSta switch { //主单成交 case preOrder.OrderType == 0 && orderStatus == 6: - handleFutMainOrderFilled(db, preOrder) + handleFutMainOrderFilled(db, preOrder, preOrder.Id, true) //止盈成交 case preOrder.OrderType == 1 && orderStatus == 6: handleTakeProfit(db, preOrder) @@ -327,22 +327,30 @@ func handleStopLoss(db *gorm.DB, preOrder *DbModels.LinePreOrder) { // 止盈单成交 func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) - removePosition(db, preOrder) + childCount, _ := GetChildTpOrder(db, preOrder.Id) - futApi := FutRestApi{} - apiUserInfo, _ := GetApiInfo(preOrder.ApiId) + if childCount > 0 { + extOrderId := preOrder.Pid //ext主单id - if apiUserInfo.Id > 0 { - if err := futApi.CancelAllFutOrder(apiUserInfo, preOrder.Symbol); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + handleFutMainOrderFilled(db, preOrder, extOrderId, false) + } else { + removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + removePosition(db, preOrder) + + futApi := FutRestApi{} + apiUserInfo, _ := GetApiInfo(preOrder.ApiId) + + if apiUserInfo.Id > 0 { + if err := futApi.CancelAllFutOrder(apiUserInfo, preOrder.Symbol); err != nil { + logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + } } - } - ids := []int{preOrder.Pid, preOrder.MainId} - //主单止盈成交 - if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND order_type=0", ids).Update("status", 9).Error; err != nil { - logger.Errorf("主单止盈成功修改主单状态失败 订单号:%s:", err) + ids := []int{preOrder.Pid, preOrder.MainId} + //主单止盈成交 + if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND order_type=0", ids).Update("status", 9).Error; err != nil { + logger.Errorf("主单止盈成功修改主单状态失败 订单号:%s:", err) + } } } @@ -396,7 +404,7 @@ func removeFutLossAndAddPosition(mainId int, orderSn string) { } // 处理主单成交,处理止盈、止损、减仓订单 -func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { +func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrderId int, first bool) { // 获取交易对配置和API信息 tradeSet, err := GetTradeSet(preOrder.Symbol, 1) if err != nil || tradeSet.Coin == "" { @@ -434,7 +442,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { // 获取和保存持仓数据 positionData := savePosition(db, preOrder) orderExt := models.LinePreOrderExt{} - db.Model(&orderExt).Where("order_id =?", preOrder.Id).First(&orderExt) + db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt) num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit)) // 更新订单数量并处理止盈、止损、减仓 @@ -443,13 +451,13 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { order.Price = price.String() // 更新止盈止损订单数量 - num = updateOrderQuantity(db, order, preOrder, num, tradeSet) + num = updateOrderQuantity(db, order, preOrder, &orderExt, num, first, tradeSet) // 根据订单类型处理 switch order.OrderType { case 1: // 止盈 //亏损大于0 重新计算比例 - if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + if first && positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { percentag := positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)) percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) order.Rate = percentag.String() @@ -460,10 +468,30 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { } else { order.Price = price.Mul(decimal.NewFromInt(1).Sub(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() } + } else if !first && orderExt.TpTpPriceRatio.Cmp(decimal.Zero) > 0 { + //止盈后止盈 + order.Rate = orderExt.TpTpPriceRatio.String() + + if positionData.PositionSide == "LONG" { + order.Price = price.Mul(decimal.NewFromInt(1).Add(orderExt.TpTpPriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + } else { + order.Price = price.Mul(decimal.NewFromInt(1).Sub(orderExt.TpTpPriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + } } processFutTakeProfitOrder(db, FutRestApi{}, order, num) case 2: // 止损 + if !first && orderExt.TpSlPriceRatio.Cmp(decimal.Zero) > 0 { + //止盈后止损 + order.Rate = orderExt.TpSlPriceRatio.String() + + if positionData.PositionSide == "LONG" { + order.Price = price.Mul(decimal.NewFromInt(1).Sub(orderExt.TpSlPriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + } else { + order.Price = price.Mul(decimal.NewFromInt(1).Add(orderExt.TpSlPriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + } + } + processFutStopLossOrder(db, order, price, num) case 4: // 减仓 processFutReduceOrder(order, price, num) @@ -541,19 +569,18 @@ func getStopOrders(db *gorm.DB, preOrder *models.LinePreOrder) ([]models.LinePre } // 更新订单数量 -func updateOrderQuantity(db *gorm.DB, order models.LinePreOrder, preOrder *models.LinePreOrder, num decimal.Decimal, tradeSet models2.TradeSet) decimal.Decimal { +func updateOrderQuantity(db *gorm.DB, order models.LinePreOrder, preOrder *models.LinePreOrder, ext *models.LinePreOrderExt, num decimal.Decimal, first bool, tradeSet models2.TradeSet) decimal.Decimal { // 处理减仓比例 - if order.OrderType == 4 { - ext := DbModels.LinePreOrderExt{} - if err := db.Model(&ext).Where("order_id=?", preOrder.Id).Find(&ext).Error; err != nil { - logger.Errorf("查询减仓比例失败, 订单号:%s, 错误信息: %v", order.OrderSn, err) - } + // if order.OrderType == 4 && ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { + // // 计算减仓数量 + // num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + // order.Num = num.String() + // } else - // 计算减仓数量 - if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { - num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) - order.Num = num.String() - } + if first && order.OrderCategory == 1 && ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { + // 计算止盈数量 + num = num.Mul(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + order.Num = num.String() } // 更新订单数量 @@ -613,17 +640,17 @@ func processFutTakeProfitOrder(db *gorm.DB, futApi FutRestApi, order models.Line if err != nil { logger.Error("合约止盈下单失败:", order.OrderSn, " err:", err) if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ?", order.Id). - Updates(map[string]interface{}{"status": "2", "desc": err.Error(), "num": params.Quantity}).Error; err != nil { + Updates(map[string]interface{}{"status": "2", "desc": err.Error(), "num": params.Quantity, "rate": order.Rate, "price": order.Price}).Error; err != nil { logger.Error("合约止盈下单失败,更新状态失败:", order.OrderSn, " err:", err) } } else { if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id). - Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate}).Error; err != nil { + Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate, "num": num.String(), "price": order.Price}).Error; err != nil { logger.Error("更新合约止盈单触发事件 ordersn:", order.OrderSn) } if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status =0", order.Id). - Updates(map[string]interface{}{"status": "1", "num": num.String(), "price": order.Price}).Error; err != nil { + Updates(map[string]interface{}{"status": "1"}).Error; err != nil { logger.Error("合约止盈下单成功,更新状态失败:", order.OrderSn, " err:", err) } } diff --git a/services/binanceservice/orderservice.go b/services/binanceservice/orderservice.go index 9fba597..5ae881a 100644 --- a/services/binanceservice/orderservice.go +++ b/services/binanceservice/orderservice.go @@ -159,3 +159,14 @@ func GetSymbolTriggerCount(db *gorm.DB, symbol string, apiId, symbolType int) (i // func GetOpenedOrders(db *gorm.DB, apiId int, exchange, symbol, symbolType, side string) ([]models.LinePreOrder, error) { // } + +// 获取子订单止盈止损数量 +func GetChildTpOrder(db *gorm.DB, pid int) (int, error) { + var count int64 + + if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type>0 and order_type <3 and status =0", pid).Count(&count).Error; err != nil { + return 0, err + } + + return int(count), nil +} diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index 3cdbb45..3a9dbb6 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -404,40 +404,41 @@ func handleMainOrderClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) // 止盈成交 func handleSpotTakeProfitFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { - removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) + childCount, _ := GetChildTpOrder(db, preOrder.Id) - spotApi := SpotRestApi{} - apiUserInfo, _ := GetApiInfo(preOrder.ApiId) + if childCount > 0 { + extOrderId := preOrder.Pid //ext主单id + positionData := savePosition(db, preOrder) + processTakeProfitAndStopLossOrders(db, preOrder, &positionData, extOrderId, false) + } else { + removeSpotLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) - if apiUserInfo.Id > 0 { - req := CancelOpenOrdersReq{ - Symbol: preOrder.Symbol, - ApiId: preOrder.ApiId, - } - if err := spotApi.CancelOpenOrders(db, req); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + spotApi := SpotRestApi{} + apiUserInfo, _ := GetApiInfo(preOrder.ApiId) + + if apiUserInfo.Id > 0 { + req := CancelOpenOrdersReq{ + Symbol: preOrder.Symbol, + ApiId: preOrder.ApiId, + } + if err := spotApi.CancelOpenOrders(db, req); err != nil { + logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + } } + + removePosition(db, preOrder) + + db.Transaction(func(tx *gorm.DB) error { + ids := []int{preOrder.Pid, preOrder.MainId} + if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6 AND order_type=0", ids).Update("status", 9).Error; err != nil { + logger.Errorf("止盈订单回调失败, 回调订单号:%s 更新主单失败:%v", preOrder.OrderSn, err) + + return err + } + + return nil + }) } - - removePosition(db, preOrder) - - db.Transaction(func(tx *gorm.DB) error { - ids := []int{preOrder.Pid, preOrder.MainId} - if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6 AND order_type=0", ids).Update("status", 9).Error; err != nil { - logger.Errorf("止盈订单回调失败, 回调订单号:%s 更新主单失败:%v", preOrder.OrderSn, err) - - return err - } - - // if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND status=0",).Update("status", 4).Error; err != nil { - // logger.Errorf("止盈订单回调失败, 回调订单号:%s 更新取消状态失败:%v", preOrder.OrderSn, err) - - // return err - // } - - return nil - }) - } // 移除仓位信息 @@ -550,7 +551,7 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } } - processTakeProfitAndStopLossOrders(db, preOrder, &positionData) + processTakeProfitAndStopLossOrders(db, preOrder, &positionData, preOrder.Id, true) } // 解析订单状态 @@ -618,7 +619,8 @@ func updateOrderStatus(db *gorm.DB, preOrder *models.LinePreOrder, status int, r // 主单成交 处理止盈止损订单 // preOrder 主单 // positionData 持仓缓存信息 -func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder, positionData *positiondto.PositionDto) { +// fist 首次止盈止损 +func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder, positionData *positiondto.PositionDto, extOrderId int, fist bool) { orders := []models.LinePreOrder{} tradeSet, _ := GetTradeSet(preOrder.Symbol, 0) @@ -651,18 +653,14 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd price := utility.StrToDecimal(preOrder.Price) spotApi := SpotRestApi{} orderExt := models.LinePreOrderExt{} - db.Model(&orderExt).Where("order_id =?", preOrder.Id).First(&orderExt) + db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt) for _, order := range orders { order.Num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)).String() - if order.OrderType == 4 { - ext := DbModels.LinePreOrderExt{} - db.Model(&ext).Where("order_id=?", preOrder.Id).Find(&ext) - - if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { - order.Num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() - } + if fist && order.OrderCategory == 1 && orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { + //主单第一次且止盈数量不是100% 止盈数量 + order.Num = num.Mul(orderExt.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() } if err := db.Model(&order).Update("num", order.Num).Error; err != nil { @@ -672,16 +670,29 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd switch order.OrderType { case 1: // 止盈 //亏损大于0 重新计算比例 - if positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { + if fist && positionData.TotalLoss.Cmp(decimal.Zero) > 0 && orderExt.Id > 0 { percentag := positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)) - percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + + if fist { + percentag = percentag.Add(orderExt.TakeProfitRatio).Truncate(2) + } + order.Rate = percentag.String() percentag = percentag.Div(decimal.NewFromInt(100)) order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag)).Truncate(int32(tradeSet.PriceDigit)).String() + } else if orderExt.Id > 0 { + percentag := orderExt.TpTpPriceRatio + order.Rate = percentag.String() + order.Price = price.Mul(decimal.NewFromInt(1).Add(percentag.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() } processTakeProfitOrder(db, spotApi, order) case 2: // 止损 + if !fist { + order.Rate = orderExt.TpSlPriceRatio.Truncate(2).String() + order.Price = price.Mul(decimal.NewFromInt(1).Sub(orderExt.TpSlPriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + } + processStopLossOrder(order) case 4: //减仓 processSpotReduceOrder(order) @@ -761,16 +772,16 @@ func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LineP if err != nil { logger.Error("现货止盈下单失败:", order.OrderSn, " err:", err) if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ?", order.Id). - Updates(map[string]interface{}{"status": "2", "desc": err.Error(), "num": params.Quantity}).Error; err != nil { + Updates(map[string]interface{}{"status": "2", "desc": err.Error(), "num": params.Quantity, "rate": order.Rate, "price": order.Price}).Error; err != nil { logger.Error("现货止盈下单失败,更新状态失败:", order.OrderSn, " err:", err) } } else { if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", order.Id). - Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate}).Error; err != nil { + Updates(map[string]interface{}{"trigger_time": time.Now(), "rate": order.Rate, "price": order.Price, "num": order.Num}).Error; err != nil { logger.Error("更新现货止盈单触发事件 ordersn:", order.OrderSn) } if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status ='0'", order.Id). - Updates(map[string]interface{}{"status": "1", "num": order.Num, "price": order.Price}).Error; err != nil { + Updates(map[string]interface{}{"status": "1"}).Error; err != nil { logger.Error("现货止盈下单成功,更新状态失败:", order.OrderSn, " err:", err) } } From 0aa2ab7355dce9908c112c8b6c63ff45f5cafc32 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Fri, 7 Mar 2025 16:48:55 +0800 Subject: [PATCH 10/13] =?UTF-8?q?=E6=8B=86=E5=88=86=E5=8A=A0=E4=BB=93?= =?UTF-8?q?=E3=80=81=E5=87=8F=E4=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/service/dto/line_pre_order.go | 16 ++- app/admin/service/line_pre_order.go | 128 ++++++++++++++++----- services/binanceservice/futuresrest.go | 104 +++++++++++++---- services/binanceservice/spotreset.go | 144 ++++++++++++++++++------ 4 files changed, 303 insertions(+), 89 deletions(-) diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index 2199b32..56a8d76 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -277,6 +277,10 @@ func (req LineAddPreOrderReq) Valid() error { return errors.New("主单减仓数量百分比不能为空") } + if req.ReducePriceRatio.IsZero() || req.ReducePriceRatio.Cmp(decimal.NewFromInt(100)) >= 0 { + return errors.New("主单减仓价格百分比错误") + } + for _, v := range req.Ext { name := "加仓" @@ -299,13 +303,17 @@ func (req LineAddPreOrderReq) Valid() error { return errors.New("止盈价格不正确") } - if v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + if v.AddType == 1 && v.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 && + v.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && + (v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0) { return errors.New("止盈后止盈价格不正确") } - if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("止盈后止损价格不正确") - } + // if v.AddType == 1 && v.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 && + // v.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && + // (v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0) { + // return errors.New("止盈后止损价格不正确") + // } } return nil diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 78af479..5e36941 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -481,10 +481,12 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP defultExt.TotalAfter = utility.StrToDecimal(AddOrder.Num).Truncate(int32(tradeSet.AmountDigit)) defultExt2.TotalBefore = defultExt.TotalAfter defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) + defultExt2.ReTakeRatio = req.ReducePriceRatio.Div(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(2) preOrderExts = append(preOrderExts, defultExt) calculateResp := dto.CalculateBreakEvenRatioResp{} mainParam := dto.CalculateBreakEevenRatioReq{ + AddType: 2, Price: mainPrice, ExchangeType: req.ExchangeType, Symbol: req.Symbol, @@ -492,8 +494,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP BuyPrice: buyPrice, LossBeginPercent: decimal.Zero, LossEndPercent: req.ReducePriceRatio, - AddPositionType: 2, - AddPositionVal: decimal.Zero, + AddPositionType: 1, + AddPositionVal: req.ReduceNumRatio, } //计算减仓后 @@ -510,14 +512,17 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP ext := models.LinePreOrderExt{ AddType: addPosition.AddType, OrderType: addPosition.OrderType, + PriceRatio: addPosition.PriceRatio, TakeProfitRatio: addPosition.TakeProfitRatio, TakeProfitNumRatio: addPosition.TakeProfitNumRatio, + StopLossRatio: addPosition.StopLossRatio, TpTpPriceRatio: addPosition.TpTpPriceRatio, TpSlPriceRatio: addPosition.TpSlPriceRatio, AddPositionType: addPosition.AddPositionType, AddPositionVal: addPosition.AddPositionVal, } + mainParam.AddType = addPosition.AddType mainParam.LossEndPercent = req.Ext[index].PriceRatio mainParam.AddPositionType = req.Ext[index].AddPositionType mainParam.AddPositionVal = req.Ext[index].AddPositionVal @@ -623,7 +628,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&stopOrder) if req.ReduceNumRatio.Cmp(decimal.Zero) > 0 && req.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { - if newOrders, err := makeReduceTakeAndStoploss(&stopOrder, defultExt, tradeSet); err != nil { + if newOrders, err := makeReduceTakeAndStoploss(&stopOrder, defultExt2, tradeSet, false); err != nil { logger.Errorf("主单减仓生成止盈、减仓失败 err:%v", err) return err } else if len(newOrders) > 0 { @@ -638,30 +643,30 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //添加止盈单 for index, v := range preOrderExts { preOrderExts[index].MainOrderId = AddOrder.Id - if index == 0 { - preOrderExts[index].OrderId = AddOrder.Id - continue - } - var AddOrder models.LinePreOrder + // if index == 0 { + // preOrderExts[index].OrderId = AddOrder.Id + // continue + // } + var newOrder models.LinePreOrder if v.AddType == 1 { - AddOrder = createPreAddPosition(&AddOrder, v, tradeSet) + newOrder = createPreAddPosition(&AddOrder, v, tradeSet) } else if v.AddType == 2 { - AddOrder = createPreReduceOrder(&AddOrder, v, tradeSet) + newOrder = createPreReduceOrder(&AddOrder, v, tradeSet) } - if AddOrder.OrderSn == "" { + if newOrder.OrderSn == "" { continue } - if err := e.Orm.Create(&AddOrder).Error; err != nil { + if err := e.Orm.Create(&newOrder).Error; err != nil { logger.Error("保存加仓单失败") return err } - preOrderExts[index].OrderId = AddOrder.Id + preOrderExts[index].OrderId = newOrder.Id //止盈、减仓 - orders, err := makeFuturesTakeAndReduce(&AddOrder, v, tradeSet) + orders, err := makeFuturesTakeAndReduce(&newOrder, v, tradeSet) if err != nil { logger.Error("构造止盈、止损失败") @@ -676,7 +681,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP for index := range orders { //减仓单且 减仓比例大于0 小于100 就冲下止盈止损 if orders[index].OrderType == 1 && v.TakeProfitRatio.Cmp(decimal.Zero) > 0 && v.TakeProfitRatio.Cmp(decimal.NewFromInt(100)) < 0 { - reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet) + reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet, true) if err != nil { logger.Error("生产止盈后止盈、减仓失败") @@ -722,6 +727,11 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx data.Pid = preOrder.Id data.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) data.MainId = preOrder.Id + + if preOrder.MainId > 0 { + data.MainId = preOrder.MainId + } + data.CreatedAt = time.Now() data.MainOrderType = v.OrderType data.Status = 0 @@ -761,12 +771,18 @@ func createPreReduceOrder(preOrder *models.LinePreOrder, ext models.LinePreOrder stopOrder.Id = 0 stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) stopOrder.Pid = preOrder.Id - stopOrder.MainId = preOrder.MainId + stopOrder.MainId = preOrder.Id + + if preOrder.MainId > 0 { + stopOrder.MainId = preOrder.MainId + } + stopOrder.OrderType = 4 stopOrder.Status = 0 stopOrder.Rate = ext.PriceRatio.String() stopOrder.Num = ext.TotalAfter.Sub(ext.TotalBefore).Abs().Truncate(int32(tradeSet.AmountDigit)).String() stopOrder.BuyPrice = "0" + stopOrder.Rate = ext.PriceRatio.String() if strings.ToUpper(preOrder.Site) == "BUY" { stopOrder.Site = "SELL" @@ -801,7 +817,11 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO profitOrder.Pid = preOrder.Id profitOrder.OrderType = 1 profitOrder.Status = 0 - profitOrder.MainId = preOrder.MainId + profitOrder.MainId = preOrder.Id + + if preOrder.MainId > 0 { + profitOrder.MainId = preOrder.MainId + } profitOrder.BuyPrice = "0" profitOrder.Site = side @@ -877,16 +897,25 @@ func makeTpOrder(parentOrder *models.LinePreOrder, reminQuantity decimal.Decimal } // 构建减仓后止盈止损 -func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { +// isTpTp 是否止盈后止盈止损 +func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet, isTpTp bool) ([]models.LinePreOrder, error) { orders := make([]models.LinePreOrder, 0) - var num decimal.Decimal + num := ext.TotalAfter + var takeProfitRatio, slPriceRatio decimal.Decimal + if isTpTp { + takeProfitRatio = ext.TpTpPriceRatio + slPriceRatio = ext.TpSlPriceRatio + } else { + takeProfitRatio = ext.TakeProfitRatio + slPriceRatio = ext.StopLossRatio + } if ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { percent := decimal.NewFromInt(1).Sub(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))) num = ext.TotalAfter.Mul(percent).Truncate(int32(tradeSet.AmountDigit)) } - if ext.TpTpPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { + if takeProfitRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { takeProfitOrder := models.LinePreOrder{} copier.Copy(&takeProfitOrder, parentOrder) takeProfitOrder.Id = 0 @@ -894,21 +923,25 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) takeProfitOrder.Status = 0 takeProfitOrder.OrderType = 1 - takeProfitOrder.Rate = ext.TpTpPriceRatio.String() takeProfitOrder.SignPrice = parentOrder.Price takeProfitOrder.CreatedAt = time.Now() takeProfitOrder.BuyPrice = "0" takeProfitOrder.MainOrderType = "LIMIT" takeProfitOrder.Num = num.String() //止盈需要累加之前的亏损 - takeProfitOrder.Rate = ext.TpTpPriceRatio.Truncate(2).String() + if isTpTp { + takeProfitOrder.Rate = takeProfitRatio.Truncate(2).String() + } else { + takeProfitOrder.Rate = takeProfitRatio.Add(ext.ReTakeRatio).Truncate(2).String() + } + takeProfitOrder.BuyPrice = "0" binanceservice.SetPrice(&takeProfitOrder, parentOrder, tradeSet) orders = append(orders, takeProfitOrder) } //有止损单 - if ext.TpSlPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { + if slPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { var stoploss models.LinePreOrder copier.Copy(&stoploss, parentOrder) @@ -920,7 +953,7 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line stoploss.OrderType = 2 stoploss.SignPrice = parentOrder.Price stoploss.BuyPrice = "0" - stoploss.Rate = ext.TpSlPriceRatio.String() + stoploss.Rate = slPriceRatio.String() stoploss.MainOrderType = "LIMIT" stoploss.Num = num.String() stoploss.BuyPrice = "0" @@ -1658,6 +1691,7 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err // ClearUnTriggered 清除待触发的交易对 func (e *LinePreOrder) ClearUnTriggered() error { var orderLists []models.LinePreOrder + positions := map[string]positiondto.LinePreOrderPositioinDelReq{} e.Orm.Model(&models.LinePreOrder{}).Where("main_id = 0 AND pid = 0 AND status = '0'").Find(&orderLists).Unscoped().Delete(&models.LinePreOrder{}) for _, order := range orderLists { @@ -1680,8 +1714,43 @@ func (e *LinePreOrder) ClearUnTriggered() error { key := fmt.Sprintf(rediskey.PreSpotOrderList, order.ExchangeType) helper.DefaultRedis.LRem(key, string(marshal)) } + + //会影响持仓的 + removeSymbolKey := fmt.Sprintf("%v_%s_%s_%s_%v", order.ApiId, order.ExchangeType, order.Symbol, order.Site, order.SymbolType) + + if _, ok := positions[removeSymbolKey]; !ok { + positions[removeSymbolKey] = positiondto.LinePreOrderPositioinDelReq{ + ApiId: order.ApiId, + Symbol: order.Symbol, + ExchangeType: order.ExchangeType, + Side: order.Site, + SymbolType: order.SymbolType, + } + } + e.Orm.Model(&models.LinePreOrder{}).Where("main_id = ?", order.Id).Unscoped().Delete(&models.LinePreOrder{}) } + + //清理仓位缓存 + for _, v := range positions { + var count int64 + e.Orm.Model(&models.LinePreOrder{}). + Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6", + v.ApiId, v.Side, v.Symbol, v.SymbolType, v.ExchangeType).Count(&count) + + //没有已开仓的订单 直接清理仓位 + if count == 0 { + var key string + + if v.SymbolType == 1 { + key = fmt.Sprintf(rediskey.SpotPosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) + } else { + key = fmt.Sprintf(rediskey.FuturePosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) + } + + helper.DefaultRedis.DeleteString(key) + } + } return nil } @@ -1774,8 +1843,8 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) error { BuyPrice: buyPrice, LossBeginPercent: lossBeginPercent, LossEndPercent: req.ReducePriceRatio, - AddPositionType: 2, - AddPositionVal: decimal.Zero, + AddPositionType: 1, + AddPositionVal: req.ReduceNumRatio, } //计算减仓后 @@ -1794,6 +1863,7 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) error { }) for index := range req.Ext { + mainParam.AddType = req.Ext[index].AddType mainParam.LossBeginPercent = lossBeginPercent mainParam.LossEndPercent = req.Ext[index].PriceRatio mainParam.AddPositionType = req.Ext[index].AddPositionType @@ -1819,9 +1889,9 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio var addPositionBuyPrice decimal.Decimal - if req.AddPositionType == 1 { + if req.AddType == 1 && req.AddPositionType == 1 { addPositionBuyPrice = req.BuyPrice.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(2) - } else { + } else if req.AddType == 1 { addPositionBuyPrice = req.AddPositionVal.Truncate(2) } @@ -1839,7 +1909,7 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100).Truncate(4))).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.PriceDigit)) //计算减仓数量 - if req.AddPositionType == 2 && req.AddPositionVal.Cmp(decimal.NewFromInt(0)) > 0 { + if req.AddType == 2 && req.AddPositionType == 1 && req.AddPositionVal.Cmp(decimal.NewFromInt(0)) > 0 { reduceAmount = totalAmount.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.AmountDigit)) } diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 6f075ac..5cd72a9 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -100,6 +100,9 @@ func handleFutOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderSta //减仓回调 case preOrder.OrderType == 4 && orderStatus == 6: handleReduceFilled(db, preOrder) + //主单取消 + case preOrder.OrderType == 0 && preOrder.Pid == 0 && orderStatus == 4: + handleMainOrderCancel(db, preOrder, 2) //止损成交 case preOrder.OrderType == 2 && orderStatus == 6: handleStopLoss(db, preOrder) @@ -154,6 +157,12 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) price := utility.StrToDecimal(preOrder.Price).Truncate(int32(tradeSet.PriceDigit)) futApi := FutRestApi{} + mainId := preOrder.Id + + if preOrder.MainId > 0 { + mainId = preOrder.MainId + } + db.Model(&orderExt).Where("order_id =?", preOrder.Pid).First(&orderExt) for _, v := range orders { @@ -177,39 +186,46 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { processFutStopLossOrder(db, v, utility.StrToDecimal(v.Price), totalNum) } } + nextFuturesReduceTrigger(db, mainId, totalNum, tradeSet) +} - //加仓待触发 - addPositionOrder := DbModels.LinePreOrder{} +// 下一个合约待触发 +func nextFuturesReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, tradeSet models2.TradeSet) { + nextOrder := DbModels.LinePreOrder{} + nextExt := DbModels.LinePreOrderExt{} - if err := db.Model(&addPositionOrder).Where("main_id =? AND order_category=3 AND status=0", preOrder.MainId).First(&addPositionOrder).Error; err != nil { - logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + if err := db.Model(&models.LinePreOrder{}).Where("main_id =? AND order_type =4 AND status=0", mainId).Order("rate asc").First(&nextOrder).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) return } - keyFutAddpositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE) - - addPositionData := AddPositionList{ - Id: addPositionOrder.Id, - OrderSn: addPositionOrder.OrderSn, - MainId: addPositionOrder.MainId, - Pid: addPositionOrder.Pid, - Price: utility.StrToDecimal(addPositionOrder.Price), - ApiId: addPositionOrder.ApiId, - Symbol: addPositionOrder.Symbol, - Side: addPositionOrder.Site, - SymbolType: addPositionOrder.SymbolType, + if err := db.Model(&models.LinePreOrderExt{}).Where("id =?", nextOrder.Id).First(&nextExt).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) } - addVal, err := sonic.MarshalString(addPositionData) + num := totalNum + //移除缓存 + key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE) + vals, _ := helper.DefaultRedis.GetAllList(key) + item := ReduceListItem{} - if err != nil { - logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) - return + for _, val := range vals { + sonic.Unmarshal([]byte(val), &item) + if item.MainId == mainId { + if _, err := helper.DefaultRedis.LRem(key, val); err != nil { + logger.Errorf("减仓单 redis删除失败 main_id:%v err:%v", mainId, err) + } + } } - if err := helper.DefaultRedis.RPushList(keyFutAddpositionKey, addVal); err != nil { - logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + // + if nextExt.AddPositionVal.Cmp(decimal.Zero) > 0 && nextExt.AddPositionVal.Cmp(decimal.Zero) < 100 { + // 计算减仓数量 + num = totalNum.Mul(nextExt.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + nextOrder.Num = num.String() } + + processFutReduceOrder(nextOrder, utility.StrToDecimal(nextOrder.Price).Truncate(int32(tradeSet.PriceDigit)), num) } // 获取合约可用数量 @@ -407,6 +423,7 @@ func removeFutLossAndAddPosition(mainId int, orderSn string) { func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrderId int, first bool) { // 获取交易对配置和API信息 tradeSet, err := GetTradeSet(preOrder.Symbol, 1) + mainId := preOrder.Id if err != nil || tradeSet.Coin == "" { logger.Errorf("获取交易对配置失败, 回调订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return @@ -418,6 +435,10 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd return } + if preOrder.MainId > 0 { + mainId = preOrder.MainId + } + // 处理主单加仓 if preOrder.OrderCategory == 3 { if err := handleMainOrderAddPosition(db, preOrder); err != nil { @@ -430,6 +451,39 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd logger.Errorf("取消主单相关订单失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return } + + //加仓待触发 + addPositionOrders := make([]DbModels.LinePreOrder, 0) + + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_category=3 AND order_type=0 AND status=0", preOrder.Id).Order("rate asc").Find(&addPositionOrders).Error; err != nil { + logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + keyFutAddpositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE) + + for _, addPositionOrder := range addPositionOrders { + addPositionData := AddPositionList{ + Id: addPositionOrder.Id, + OrderSn: addPositionOrder.OrderSn, + MainId: addPositionOrder.MainId, + Pid: addPositionOrder.Pid, + Price: utility.StrToDecimal(addPositionOrder.Price), + ApiId: addPositionOrder.ApiId, + Symbol: addPositionOrder.Symbol, + Side: addPositionOrder.Site, + SymbolType: addPositionOrder.SymbolType, + } + + addVal, err := sonic.MarshalString(addPositionData) + + if err != nil { + logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + return + } + + if err := helper.DefaultRedis.RPushList(keyFutAddpositionKey, addVal); err != nil { + logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + } } // 获取止盈止损订单 @@ -493,10 +547,12 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd } processFutStopLossOrder(db, order, price, num) - case 4: // 减仓 - processFutReduceOrder(order, price, num) + // case 4: // 减仓 + // processFutReduceOrder(order, price, num) } } + + nextFuturesReduceTrigger(db, mainId, num, tradeSet) } // 处理主单加仓 diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index 3a9dbb6..6718086 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -119,7 +119,7 @@ func handleOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderStatus handleMainReduceFilled(db, preOrder) //主单取消 case preOrder.OrderType == 0 && preOrder.Pid == 0 && orderStatus == 4: - handleMainOrderCancel(preOrder) + handleMainOrderCancel(db, preOrder, 1) // 止盈成交 case preOrder.OrderType == 1 && orderStatus == 6: handleSpotTakeProfitFilled(db, preOrder) @@ -224,38 +224,57 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } } - //加仓待触发 - addPositionOrder := DbModels.LinePreOrder{} + mainId := preOrder.Id - if err := db.Model(&addPositionOrder).Where("main_id =? AND order_category=3 AND order_type=0 AND status=0", preOrder.MainId).First(&addPositionOrder).Error; err != nil { - logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) - return + if preOrder.MainId > 0 { + mainId = preOrder.MainId } - keySpotAddPosition := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) + nextSpotReduceTrigger(db, mainId, totalNum, tradeSet) +} - addPositionData := AddPositionList{ - Id: addPositionOrder.Id, - OrderSn: addPositionOrder.OrderSn, - MainId: addPositionOrder.MainId, - Pid: addPositionOrder.Pid, - Price: utility.StrToDecimal(addPositionOrder.Price), - ApiId: addPositionOrder.ApiId, - Symbol: addPositionOrder.Symbol, - Side: addPositionOrder.Site, - SymbolType: addPositionOrder.SymbolType, +// 缓存下一个减仓单 +func nextSpotReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, tradeSet models2.TradeSet) bool { + nextOrder := DbModels.LinePreOrder{} + nextExt := DbModels.LinePreOrderExt{} + // var percentag decimal.Decimal + + if err := db.Model(&models.LinePreOrder{}).Where("main_id =? AND order_type =4 AND status=0", mainId).Order("rate asc").First(&nextOrder).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) + return true } - addVal, err := sonic.MarshalString(addPositionData) - - if err != nil { - logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) - return + if err := db.Model(&models.LinePreOrderExt{}).Where("id =?", nextOrder.Id).First(&nextExt).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) } - if err := helper.DefaultRedis.RPushList(keySpotAddPosition, addVal); err != nil { - logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + //移除缓存 + key := fmt.Sprintf(rediskey.SpotReduceList, global.EXCHANGE_BINANCE) + vals, _ := helper.DefaultRedis.GetAllList(key) + item := ReduceListItem{} + + for _, val := range vals { + sonic.Unmarshal([]byte(val), &item) + if item.MainId == mainId { + if _, err := helper.DefaultRedis.LRem(key, val); err != nil { + logger.Errorf("减仓单 redis删除失败 main_id:%v err:%v", mainId, err) + } + } } + + //减仓配置 且减仓比例大于0小于100 + if nextExt.Id > 0 && nextExt.AddType == 2 && nextExt.AddPositionVal.Cmp(decimal.Zero) > 0 && nextExt.AddPositionVal.Cmp(decimal.Zero) < 100 { + num := totalNum.Mul(nextExt.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + // percentag = positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)).Truncate(2) + nextOrder.Num = num.String() + } + // percentag = nextExt.PriceRatio.Add(percentag) + // nextOrder.Rate = percentag.String() + // nextOrder.Price = price.Mul(decimal.NewFromInt(1).Add(percentag.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.AmountDigit)).String() + + //减仓待触发 + processSpotReduceOrder(nextOrder) + return false } // 获取主单可用数量 @@ -344,8 +363,16 @@ func getSpotPositionNum(apiUserInfo DbModels.LineApiUser, preOrder *DbModels.Lin } // 主单取消 -func handleMainOrderCancel(preOrder *DbModels.LinePreOrder) { - preOrderKey := fmt.Sprintf(rediskey.PreSpotOrderList, global.EXCHANGE_BINANCE) +// symbolType 1:现货 2:合约 +func handleMainOrderCancel(db *gorm.DB, preOrder *DbModels.LinePreOrder, symboType int) { + var preOrderKey string + + if symboType == 1 { + preOrderKey = fmt.Sprintf(rediskey.PreSpotOrderList, global.EXCHANGE_BINANCE) + } else { + preOrderKey = fmt.Sprintf(rediskey.PreFutOrderList, global.EXCHANGE_BINANCE) + } + preSpotOrders, _ := helper.DefaultRedis.GetAllList(preOrderKey) preOrderCache := DbModels.LinePreOrder{} @@ -356,10 +383,17 @@ func handleMainOrderCancel(preOrder *DbModels.LinePreOrder) { sonic.Unmarshal([]byte(v), &preOrderCache) if preOrderCache.Pid == preOrder.Id { - _, err := helper.DefaultRedis.LRem(preOrderKey, v) + var count int64 + db.Model(&models.LinePreOrder{}). + Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6", + preOrder.ApiId, preOrder.Site, preOrder.Symbol, preOrder.SymbolType, preOrder.ExchangeType).Count(&count) - if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除缓存失败:%v", preOrder.OrderSn, err) + if count == 0 { + _, err := helper.DefaultRedis.LRem(preOrderKey, v) + + if err != nil { + logger.Errorf("订单回调失败, 回调订单号:%s 删除缓存失败:%v", preOrder.OrderSn, err) + } } } } @@ -549,9 +583,45 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } } } + + //加仓待触发 + addPositionOrders := make([]DbModels.LinePreOrder, 0) + + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_category=3 AND order_type=0 AND status=0", preOrder.Id).Order("rate asc").Find(&addPositionOrders).Error; err != nil { + logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + + keySpotAddPosition := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) + + for _, addPositionOrder := range addPositionOrders { + addPositionData := AddPositionList{ + Id: addPositionOrder.Id, + OrderSn: addPositionOrder.OrderSn, + MainId: addPositionOrder.MainId, + Pid: addPositionOrder.Pid, + Price: utility.StrToDecimal(addPositionOrder.Price), + ApiId: addPositionOrder.ApiId, + Symbol: addPositionOrder.Symbol, + Side: addPositionOrder.Site, + SymbolType: addPositionOrder.SymbolType, + } + + addVal, err := sonic.MarshalString(addPositionData) + + if err != nil { + logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + return + } + + if err := helper.DefaultRedis.RPushList(keySpotAddPosition, addVal); err != nil { + logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + } + } processTakeProfitAndStopLossOrders(db, preOrder, &positionData, preOrder.Id, true) + } // 解析订单状态 @@ -623,6 +693,11 @@ func updateOrderStatus(db *gorm.DB, preOrder *models.LinePreOrder, status int, r func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder, positionData *positiondto.PositionDto, extOrderId int, fist bool) { orders := []models.LinePreOrder{} tradeSet, _ := GetTradeSet(preOrder.Symbol, 0) + mainId := preOrder.Id + + if preOrder.MainId > 0 { + mainId = preOrder.MainId + } if tradeSet.Coin == "" { logger.Error("获取交易对失败") @@ -654,9 +729,11 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd spotApi := SpotRestApi{} orderExt := models.LinePreOrderExt{} db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt) + num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)) + //止盈止损 for _, order := range orders { - order.Num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)).String() + order.Num = num.String() if fist && order.OrderCategory == 1 && orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { //主单第一次且止盈数量不是100% 止盈数量 @@ -694,10 +771,13 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd } processStopLossOrder(order) - case 4: //减仓 - processSpotReduceOrder(order) + // case 4: //减仓 + // processSpotReduceOrder(order) } } + + //待触发减仓单 + nextSpotReduceTrigger(db, mainId, num, tradeSet) } // 根据下单百分比计算价格 From 00aa341c2f9a64304d3d00ccae0bba6391a439d9 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Mon, 10 Mar 2025 11:13:08 +0800 Subject: [PATCH 11/13] 1 --- app/admin/service/dto/line_pre_order.go | 53 ++++++------ app/admin/service/line_pre_order.go | 85 ++++++++++++++++--- common/const/rediskey/redis_key.go | 7 +- services/binanceservice/commonservice.go | 2 +- .../binanceservice/futuresjudgeservice.go | 17 ++-- services/binanceservice/futuresrest.go | 59 +++++++------ services/binanceservice/spotjudgeservice.go | 16 ++-- services/binanceservice/spotreset.go | 26 +++--- 8 files changed, 176 insertions(+), 89 deletions(-) diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index 56a8d76..4166c36 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -353,31 +353,34 @@ type LineTreeOrder struct { // LineBatchAddPreOrderReq 批量添加订单请求参数 type LineBatchAddPreOrderReq struct { - ExchangeType string `json:"exchange_type"` //交易所类型 字典exchange_type - SymbolType int `json:"symbol_type"` //主单交易对类型 0-现货 1-合约 - OrderType int `json:"order_type"` //订单类型 - SymbolGroupId string `json:"symbol_group_id"` //交易对组id - Symbol string `json:"symbol"` //交易对 - ApiUserId string `json:"api_id"` //下单用户 - Site string `json:"site"` //购买方向 - BuyPrice string `json:"buy_price"` //购买金额 U - PricePattern string `json:"price_pattern"` //价格模式 - Price string `json:"price"` //下单价百分比 - Profit string `json:"profit"` //止盈价 - StopPrice string `json:"stop_price"` //止损价 - PriceType string `json:"price_type"` //价格类型 - SaveTemplate string `json:"save_template"` //是否保存模板 - TemplateName string `json:"template_name"` //模板名字 - OrderNum int `json:"order_num"` //脚本运行次数 - Script string `json:"script"` //是否是脚本运行 1 = 是 0= 否 - CoverType int `json:"cover_type"` //对冲类型 1= 现货对合约 2=合约对合约 3 合约对现货 - ExpireHour int `json:"expire_hour"` // 过期时间 单位小时 - MainOrderType string `json:"main_order_type"` //主单类型:限价(LIMIT)或市价(MARKET) - ReducePriceRatio decimal.Decimal `json:"reduce_price"` //主单减仓价格百分比 - ReduceNumRatio decimal.Decimal `json:"reduce_num"` //主单减仓数量百分比 - ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比 - ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比 - Ext []LineAddPreOrderExtReq `json:"ext"` //拓展字段 + ExchangeType string `json:"exchange_type"` //交易所类型 字典exchange_type + SymbolType int `json:"symbol_type"` //主单交易对类型 0-现货 1-合约 + OrderType int `json:"order_type"` //订单类型 + SymbolGroupId string `json:"symbol_group_id"` //交易对组id + Symbol string `json:"symbol"` //交易对 + ApiUserId string `json:"api_id"` //下单用户 + Site string `json:"site"` //购买方向 + BuyPrice string `json:"buy_price"` //购买金额 U + PricePattern string `json:"price_pattern"` //价格模式 + Price string `json:"price"` //下单价百分比 + ProfitNumRatio decimal.Decimal `json:"profit_num_ratio"` //止盈数量百分比 + ProfitTpTpPriceRatio decimal.Decimal `json:"profit_tp_tp_price_ratio"` //止盈后止盈价百分比 + ProfitTpSlPriceRatio decimal.Decimal `json:"profit_tp_sl_price_ratio"` //止盈后止损价百分比 + Profit string `json:"profit"` //止盈价 + StopPrice string `json:"stop_price"` //止损价 + PriceType string `json:"price_type"` //价格类型 + SaveTemplate string `json:"save_template"` //是否保存模板 + TemplateName string `json:"template_name"` //模板名字 + OrderNum int `json:"order_num"` //脚本运行次数 + Script string `json:"script"` //是否是脚本运行 1 = 是 0= 否 + CoverType int `json:"cover_type"` //对冲类型 1= 现货对合约 2=合约对合约 3 合约对现货 + ExpireHour int `json:"expire_hour"` // 过期时间 单位小时 + MainOrderType string `json:"main_order_type"` //主单类型:限价(LIMIT)或市价(MARKET) + ReducePriceRatio decimal.Decimal `json:"reduce_price"` //主单减仓价格百分比 + ReduceNumRatio decimal.Decimal `json:"reduce_num"` //主单减仓数量百分比 + ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比 + ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比 + Ext []LineAddPreOrderExtReq `json:"ext"` //拓展字段 } func (req LineBatchAddPreOrderReq) CheckParams() error { diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 5e36941..96ebcc9 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -244,7 +244,49 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi } positions := map[string]positiondto.LinePreOrderPositioinDelReq{} + addPosition := binanceservice.AddPositionList{} + reduceItem := binanceservice.ReduceListItem{} + futAddPosition := map[int]string{} + spotAddPosition := map[int]string{} + futReduces := map[int]string{} + spotRedces := map[int]string{} + futAddPositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE) + spotAddPositionKey := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE) + futReduceKey := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) + spotReduceKey := fmt.Sprintf(rediskey.SpotReduceList, global.EXCHANGE_BINANCE) + futAddPositionVal, _ := helper.DefaultRedis.GetAllList(futAddPositionKey) + futReduceVal, _ := helper.DefaultRedis.GetAllList(spotAddPositionKey) + spotAddPositionVal, _ := helper.DefaultRedis.GetAllList(futReduceKey) + spotReduceVal, _ := helper.DefaultRedis.GetAllList(spotReduceKey) + + for _, v := range futAddPositionVal { + sonic.Unmarshal([]byte(v), &addPosition) + if addPosition.MainId > 0 { + futAddPosition[addPosition.MainId] = v + } + } + + for _, v := range spotAddPositionVal { + sonic.Unmarshal([]byte(v), &addPosition) + if addPosition.MainId > 0 { + spotAddPosition[addPosition.MainId] = v + } + } + + for _, v := range futReduceVal { + sonic.Unmarshal([]byte(v), &reduceItem) + if reduceItem.MainId > 0 { + futReduces[reduceItem.MainId] = v + } + } + + for _, v := range spotReduceVal { + sonic.Unmarshal([]byte(v), &reduceItem) + if reduceItem.MainId > 0 { + spotRedces[reduceItem.MainId] = v + } + } //删除的缓存 for _, order := range list { redisList := dto.PreOrderRedisList{ @@ -261,6 +303,21 @@ func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermi apiIds = append(apiIds, order.ApiId) } + //清除待加仓、待减仓 + if val, ok := futAddPosition[order.Id]; ok { + helper.DefaultRedis.LRem(futAddPositionKey, val) + } + + if val, ok := spotAddPosition[order.Id]; ok { + helper.DefaultRedis.LRem(spotAddPositionKey, val) + } + if val, ok := futReduces[order.Id]; ok { + helper.DefaultRedis.LRem(futReduceKey, val) + } + if val, ok := spotRedces[order.Id]; ok { + helper.DefaultRedis.LRem(spotReduceKey, val) + } + tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, fmt.Sprintf(global.TICKER_SPOT, order.ExchangeType, order.Symbol)) redisList.Price = utility.StringToDecimal(redisList.Price).Truncate(int32(tradeSet.PriceDigit)).String() marshal, _ := sonic.Marshal(redisList) @@ -483,6 +540,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) defultExt2.ReTakeRatio = req.ReducePriceRatio.Div(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(2) preOrderExts = append(preOrderExts, defultExt) + preOrderExts = append(preOrderExts, defultExt2) calculateResp := dto.CalculateBreakEvenRatioResp{} mainParam := dto.CalculateBreakEevenRatioReq{ @@ -550,7 +608,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //加仓、减仓状态 tx.Model(&models.LinePreOrderStatus{}).Create(&preOrderStatus) - + preOrderExts[0].OrderId = AddOrder.Id list := dto.PreOrderRedisList{ Id: AddOrder.Id, Symbol: AddOrder.Symbol, @@ -626,7 +684,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP stopOrder.Num = stopNum.Truncate(int32(tradeSet.AmountDigit)).String() tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&stopOrder) - + preOrderExts[1].OrderId = stopOrder.Id if req.ReduceNumRatio.Cmp(decimal.Zero) > 0 && req.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { if newOrders, err := makeReduceTakeAndStoploss(&stopOrder, defultExt2, tradeSet, false); err != nil { logger.Errorf("主单减仓生成止盈、减仓失败 err:%v", err) @@ -643,10 +701,10 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //添加止盈单 for index, v := range preOrderExts { preOrderExts[index].MainOrderId = AddOrder.Id - // if index == 0 { - // preOrderExts[index].OrderId = AddOrder.Id - // continue - // } + if index < 2 { + // preOrderExts[index].OrderId = AddOrder.Id + continue + } var newOrder models.LinePreOrder if v.AddType == 1 { @@ -801,10 +859,10 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO orders := make([]models.LinePreOrder, 0) var side string - if strings.ToUpper(preOrder.Site) == "BUY" { - side = "SELL" - } else { + if (preOrder.OrderType != 0 && strings.ToUpper(preOrder.Site) == "BUY") || (preOrder.OrderType == 0 && strings.ToUpper(preOrder.Site) == "SELL") { side = "BUY" + } else { + side = "SELL" } if ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 { @@ -1055,6 +1113,9 @@ func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p req.PricePattern = batchReq.PricePattern req.Price = batchReq.Price req.Profit = batchReq.Profit + req.ProfitNumRatio = batchReq.ProfitNumRatio + req.ProfitTpTpPriceRatio = batchReq.ProfitTpSlPriceRatio + req.ProfitTpSlPriceRatio = batchReq.ProfitTpSlPriceRatio req.Ext = batchReq.Ext req.SymbolType = batchReq.SymbolType // req.StopPrice = batchReq.StopPrice @@ -1663,8 +1724,10 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err continue } - //撤销合约的委托 - api.CancelAllFutOrder(apiUserInfo, list.Symbol) + //撤销合约的委托(根据方向撤) + orderSns, _ := binanceservice.GetOpenOrderSns(e.Orm, []int{list.Id}) + api.CancelBatchFutOrder(apiUserInfo, list.Symbol, orderSns) + // api.CancelAllFutOrder(apiUserInfo, list.Symbol) //side=BUY&positionSide=LONG是开多, //side=SELL&positionSide=LONG是平多, //side=SELL&positionSide=SHORT是开空, diff --git a/common/const/rediskey/redis_key.go b/common/const/rediskey/redis_key.go index 820f41b..56d5372 100644 --- a/common/const/rediskey/redis_key.go +++ b/common/const/rediskey/redis_key.go @@ -50,9 +50,10 @@ const ( SpotReduceList = "spot_reduce_list:%s" //现货减仓待触发 {交易所类型code} FuturesStopLossList = "futures_stoploss_list:%s" //合约止损待触发列表 {交易所类型code} FuturesReduceList = "futures_reduce_list:%s" //合约减仓待触发 {交易所类型code} - - SpotAddPositionList = "spot_add_position_list:%s" //现货加仓待触发 {交易所code} - FuturesAddPositionList = "futures_add_position_list:%s" //合约加仓待触发 {交易所code} + //现货加仓待触发 {交易所code} + SpotAddPositionList = "spot_add_position_list:%s" + //合约加仓待触发 {交易所code} + FuturesAddPositionList = "futures_add_position_list:%s" //现货持仓 {exchangeType,apiuserid,symbol,side} SpotPosition = "spot_position:%s:%v:%s_%s" //合约持仓 {exchangeType,apiuserid,symbol,side} diff --git a/services/binanceservice/commonservice.go b/services/binanceservice/commonservice.go index adcd648..8a47ac0 100644 --- a/services/binanceservice/commonservice.go +++ b/services/binanceservice/commonservice.go @@ -471,7 +471,7 @@ func getOpenPositionMainOrderId(db *gorm.DB, newId, apiId, symbolType int, excha } // 获取需要取消的订单号 -func getOpenOrderSns(db *gorm.DB, mainIds []int) ([]string, error) { +func GetOpenOrderSns(db *gorm.DB, mainIds []int) ([]string, error) { result := []string{} //委托中的订单 diff --git a/services/binanceservice/futuresjudgeservice.go b/services/binanceservice/futuresjudgeservice.go index 1c098ce..1d53614 100644 --- a/services/binanceservice/futuresjudgeservice.go +++ b/services/binanceservice/futuresjudgeservice.go @@ -201,8 +201,8 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes return } else if ok { defer lock.Release() - takeOrder := DbModels.LinePreOrder{} - if err := db.Model(&DbModels.LinePreOrder{}).Where("pid =? AND order_type =1", reduceOrder.Pid).Find(&takeOrder).Error; err != nil { + takeOrders := make([]DbModels.LinePreOrder, 0) + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type =1 AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil { log.Error("查询止盈单失败") return } @@ -214,18 +214,19 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes return } - apiInfo, _ := GetApiInfo(takeOrder.ApiId) + apiInfo, _ := GetApiInfo(reduceOrder.ApiId) if apiInfo.Id == 0 { log.Error("现货减仓 查询api用户不存在") return } + for _, takeOrder := range takeOrders { + err := CancelFutOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) - err := CancelFutOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) - - if err != nil { - log.Error("合约止盈撤单失败", err) - return + if err != nil { + log.Error("合约止盈撤单失败", err) + return + } } price := reduceOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.ReducePremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 5cd72a9..8d4b76b 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -154,7 +154,6 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { orderExt := models.LinePreOrderExt{} totalNum := getFuturesPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) - totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) price := utility.StrToDecimal(preOrder.Price).Truncate(int32(tradeSet.PriceDigit)) futApi := FutRestApi{} mainId := preOrder.Id @@ -199,7 +198,7 @@ func nextFuturesReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, return } - if err := db.Model(&models.LinePreOrderExt{}).Where("id =?", nextOrder.Id).First(&nextExt).Error; err != nil { + if err := db.Model(&models.LinePreOrderExt{}).Where("order_id =? and add_type =2", nextOrder.Id).First(&nextExt).Error; err != nil { logger.Errorf("获取下一个单失败 err:%v", err) } @@ -223,6 +222,10 @@ func nextFuturesReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, // 计算减仓数量 num = totalNum.Mul(nextExt.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) nextOrder.Num = num.String() + + if err := db.Model(&nextOrder).Update("num", nextOrder.Num).Error; err != nil { + logger.Errorf("更新减仓单数量失败 err:%v", err) + } } processFutReduceOrder(nextOrder, utility.StrToDecimal(nextOrder.Price).Truncate(int32(tradeSet.PriceDigit)), num) @@ -321,16 +324,12 @@ func handleStopLoss(db *gorm.DB, preOrder *DbModels.LinePreOrder) { removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) removePosition(db, preOrder) - spotApi := SpotRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) if apiUserInfo.Id > 0 { - req := CancelOpenOrdersReq{ - Symbol: preOrder.Symbol, - ApiId: preOrder.ApiId, - } - if err := spotApi.CancelOpenOrders(db, req); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + mainIds := []int{preOrder.MainId} + if err := cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil { + logger.Errorf("止损单成功 取消其它订单失败 订单号:%s:", err) } } @@ -353,12 +352,12 @@ func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) removePosition(db, preOrder) - futApi := FutRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) if apiUserInfo.Id > 0 { - if err := futApi.CancelAllFutOrder(apiUserInfo, preOrder.Symbol); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + mainOrder, _ := GetOrderById(db, preOrder.MainId) + if err := cancelPositionOtherOrders(apiUserInfo, db, &mainOrder, false); err != nil { + logger.Errorf("止损单成功 取消其它订单失败 订单号:%s:", err) } } @@ -445,9 +444,9 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd logger.Errorf("处理主单加仓失败, 主单号:%s, 错误信息: %v", preOrder.MainId, err) return } - } else { + } else if first { // 处理其他主单逻辑 - if err := cancelPositionOtherOrders(apiInfo, db, preOrder); err != nil { + if err := cancelPositionOtherOrders(apiInfo, db, preOrder, true); err != nil { logger.Errorf("取消主单相关订单失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return } @@ -497,7 +496,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd positionData := savePosition(db, preOrder) orderExt := models.LinePreOrderExt{} db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt) - num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit)) + totalNum := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet).Truncate(int32(tradeSet.AmountDigit)) // 更新订单数量并处理止盈、止损、减仓 for _, order := range orders { @@ -505,7 +504,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd order.Price = price.String() // 更新止盈止损订单数量 - num = updateOrderQuantity(db, order, preOrder, &orderExt, num, first, tradeSet) + num := updateOrderQuantity(db, order, preOrder, &orderExt, totalNum, first, tradeSet) // 根据订单类型处理 switch order.OrderType { @@ -552,7 +551,7 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd } } - nextFuturesReduceTrigger(db, mainId, num, tradeSet) + nextFuturesReduceTrigger(db, mainId, totalNum, tradeSet) } // 处理主单加仓 @@ -569,7 +568,8 @@ func handleMainOrderAddPosition(db *gorm.DB, preOrder *models.LinePreOrder) erro } // 取消主单相关订单 -func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, preOrder *models.LinePreOrder) error { +// changeMainOrderStatus 是否修改主单状态 +func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, preOrder *models.LinePreOrder, changeMainOrderStatus bool) error { mainOrders, err := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) if err != nil { return err @@ -589,25 +589,36 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr } } + // 批量取消订单 + err = cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, changeMainOrderStatus) + + return err +} + +// 根据mainid 取消订单 +// @changeMainOrderStatus 是否更新主单状态 +func cancelMainOrders(mainIds []int, db *gorm.DB, apiUserInfo DbModels.LineApiUser, symbol string, changeMainOrderStatus bool) error { if len(mainIds) > 0 { - orderSns, err := getOpenOrderSns(db, mainIds) + orderSns, err := GetOpenOrderSns(db, mainIds) if err != nil { return err } - // 批量取消订单 orderArray := utility.SplitSlice(orderSns, 10) futApi := FutRestApi{} for _, item := range orderArray { - err := futApi.CancelBatchFutOrder(apiUserInfo, preOrder.Symbol, item) + err := futApi.CancelBatchFutOrder(apiUserInfo, symbol, item) if err != nil { logger.Errorf("批量取消订单失败 orderSns:%v", item) } } - if err := db.Exec("UPDATE line_pre_order SET `status`=4, `desc`=CONCAT(`desc`, ' 新单触发取消') WHERE id IN ? AND `status` =6", mainIds).Error; err != nil { - logger.Errorf("合约 新下单成功后更新主单取消状态失败, 新主单号:%s, 错误信息:%v", preOrder.MainId, err) + //需要修改主单状态 + if changeMainOrderStatus { + if err := db.Exec("UPDATE line_pre_order SET `status`=4, `desc`=CONCAT(`desc`, ' 新单触发取消') WHERE id IN ? AND `status` =6", mainIds).Error; err != nil { + logger.Errorf("合约 新下单成功后更新主单取消状态失败, 交易对:%s, 错误信息:%v", symbol, err) + } } } return nil @@ -633,7 +644,7 @@ func updateOrderQuantity(db *gorm.DB, order models.LinePreOrder, preOrder *model // order.Num = num.String() // } else - if first && order.OrderCategory == 1 && ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { + if first && (order.OrderCategory == 1 || order.OrderCategory == 3) && order.OrderType == 1 && ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { // 计算止盈数量 num = num.Mul(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) order.Num = num.String() diff --git a/services/binanceservice/spotjudgeservice.go b/services/binanceservice/spotjudgeservice.go index 047e5cd..ff85199 100644 --- a/services/binanceservice/spotjudgeservice.go +++ b/services/binanceservice/spotjudgeservice.go @@ -308,8 +308,8 @@ func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRest return } else if ok { defer lock.Release() - takeOrder := DbModels.LinePreOrder{} - if err := db.Model(&DbModels.LinePreOrder{}).Where("pid =? AND order_type =1", reduceOrder.Pid).Find(&takeOrder).Error; err != nil { + takeOrders := make([]DbModels.LinePreOrder, 0) + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type =1 AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil { log.Error("查询止盈单失败") return } @@ -320,18 +320,20 @@ func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRest return } - apiInfo, _ := GetApiInfo(takeOrder.ApiId) + apiInfo, _ := GetApiInfo(reduceOrder.ApiId) if apiInfo.Id == 0 { log.Error("现货减仓 查询api用户不存在") return } - err := CancelOpenOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) + for _, takeOrder := range takeOrders { + err := CancelOpenOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) - if err != nil { - log.Error("现货止盈撤单失败", err) - return + if err != nil { + log.Error("现货止盈撤单失败", err) + return + } } price := reduceOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.ReducePremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index 6718086..ddcbfad 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -195,7 +195,7 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { db.Model(&orderExt).Where("order_id =?", preOrder.Pid).Find(&orderExt) totalNum := getSpotPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) //getSpotTotalNum(apiUserInfo, preOrder, tradeSet) - totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) + totalNum = totalNum.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)) price := utility.StrToDecimal(preOrder.Price) if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status=0", preOrder.Id).Find(&orders).Error; err != nil { @@ -244,7 +244,7 @@ func nextSpotReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, tr return true } - if err := db.Model(&models.LinePreOrderExt{}).Where("id =?", nextOrder.Id).First(&nextExt).Error; err != nil { + if err := db.Model(&models.LinePreOrderExt{}).Where("order_id =? and add_type =2", nextOrder.Id).First(&nextExt).Error; err != nil { logger.Errorf("获取下一个单失败 err:%v", err) } @@ -265,12 +265,12 @@ func nextSpotReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, tr //减仓配置 且减仓比例大于0小于100 if nextExt.Id > 0 && nextExt.AddType == 2 && nextExt.AddPositionVal.Cmp(decimal.Zero) > 0 && nextExt.AddPositionVal.Cmp(decimal.Zero) < 100 { num := totalNum.Mul(nextExt.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) - // percentag = positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)).Truncate(2) nextOrder.Num = num.String() + + if err := db.Model(&nextOrder).Update("num", nextOrder.Num).Error; err != nil { + logger.Errorf("更新减仓单数量失败 err:%v", err) + } } - // percentag = nextExt.PriceRatio.Add(percentag) - // nextOrder.Rate = percentag.String() - // nextOrder.Price = price.Mul(decimal.NewFromInt(1).Add(percentag.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.AmountDigit)).String() //减仓待触发 processSpotReduceOrder(nextOrder) @@ -638,7 +638,7 @@ func parseOrderStatus(status interface{}, mapData map[string]interface{}) (int, return 5, reason case "FILLED": // 完全成交 return 6, reason - case "CANCELED", "EXPIRED": // 取消 + case "CANCELED", "EXPIRED", "EXPIRED_IN_MATCH": // 取消 return 4, reason default: return 0, reason @@ -729,13 +729,17 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd spotApi := SpotRestApi{} orderExt := models.LinePreOrderExt{} db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt) - num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)) + totalNum := num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)) + num = totalNum + // if orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { + // num = num.Mul(orderExt.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + // } //止盈止损 for _, order := range orders { order.Num = num.String() - if fist && order.OrderCategory == 1 && orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { + if fist && order.OrderCategory == 1 && order.OrderType == 1 && orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { //主单第一次且止盈数量不是100% 止盈数量 order.Num = num.Mul(orderExt.TakeProfitNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() } @@ -777,7 +781,7 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd } //待触发减仓单 - nextSpotReduceTrigger(db, mainId, num, tradeSet) + nextSpotReduceTrigger(db, mainId, totalNum, tradeSet) } // 根据下单百分比计算价格 @@ -947,6 +951,8 @@ func CancelOpenOrderByOrderSnLoop(apiInfo DbModels.LineApiUser, symbol string, o err = nil break } + + time.Sleep(time.Millisecond * 300) } return err } From ddba8e4ce21dda51b782e9fdbd2368777423aaf0 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Mon, 10 Mar 2025 18:02:54 +0800 Subject: [PATCH 12/13] 1 --- services/binanceservice/futuresrest.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 8d4b76b..0d2ce5b 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -355,8 +355,8 @@ func handleTakeProfit(db *gorm.DB, preOrder *DbModels.LinePreOrder) { apiUserInfo, _ := GetApiInfo(preOrder.ApiId) if apiUserInfo.Id > 0 { - mainOrder, _ := GetOrderById(db, preOrder.MainId) - if err := cancelPositionOtherOrders(apiUserInfo, db, &mainOrder, false); err != nil { + mainIds := []int{preOrder.MainId} + if err := cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil { logger.Errorf("止损单成功 取消其它订单失败 订单号:%s:", err) } } @@ -573,6 +573,8 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr mainOrders, err := getOpenPositionMainOrderId(db, preOrder.Id, preOrder.ApiId, preOrder.SymbolType, preOrder.ExchangeType, preOrder.Symbol, preOrder.Site) if err != nil { return err + } else if len(mainOrders) == 0 { + logger.Infof("主单没有持仓,不需要取消其他订单 sn:%s", preOrder.OrderSn) } mainIds := []int{} @@ -590,6 +592,7 @@ func cancelPositionOtherOrders(apiUserInfo DbModels.LineApiUser, db *gorm.DB, pr } // 批量取消订单 + err = cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, changeMainOrderStatus) return err From ffb12cdb64cff1291db7fdc5389d8476aa2081c3 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Tue, 11 Mar 2025 15:51:40 +0800 Subject: [PATCH 13/13] 1 --- app/admin/service/dto/line_pre_order.go | 11 ++-- app/admin/service/line_pre_order.go | 73 ++++++++++++++++--------- pkg/utility/decimalhelper.go | 13 +++++ services/binanceservice/futuresrest.go | 6 +- services/scriptservice/order.go | 15 +++-- 5 files changed, 80 insertions(+), 38 deletions(-) diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index 4166c36..fbcbf25 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -437,13 +437,15 @@ func (req LineBatchAddPreOrderReq) CheckParams() error { return errors.New("止盈价格不正确") } - if v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + if v.AddType == 1 && v.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 && + v.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && + (v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0) { return errors.New("止盈后止盈价格不正确") } - if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("止盈后止损价格不正确") - } + // if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + // return errors.New("止盈后止损价格不正确") + // } } return nil @@ -582,6 +584,7 @@ type ClosePosition struct { Rate string `json:"rate"` //限价平仓百分比 CloseType int `json:"close_type"` //现货平仓=1 合约平仓=2 ExchangeType string `json:"exchange_type"` //交易所类型 字典exchange_type + Side string `json:"side"` //SELL=卖出(空) BUY=买入(多) } func (m *ClosePosition) CheckParams() error { diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 96ebcc9..c771b50 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -443,8 +443,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP if req.PricePattern == "percentage" { AddOrder.Rate = req.Price - orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10% - priceRate := orderPrice.Div(decimal.NewFromInt(100)) //下单价除100 =0.1 + orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10% + priceRate := utility.SafeDiv(orderPrice, (decimal.NewFromInt(100))) //下单价除100 =0.1 if strings.ToUpper(req.Site) == "BUY" { //购买方向 //实际下单价格 @@ -486,12 +486,12 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365 //在换算成对应交易对对应的价值 //LTCBTC --> LTCUSDT == LTCUSDT -- 100.502 - div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice)) + div := utility.SafeDiv(utility.SafeDiv(tickerPrice, decimal.NewFromInt(1)), uTickerPrice) //计算下单数量 - AddOrder.Num = buyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String() + AddOrder.Num = utility.SafeDiv(buyPrice, div).Truncate(int32(tradeSet.AmountDigit)).String() } else { fromString, _ := decimal.NewFromString(AddOrder.Price) - AddOrder.Num = buyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String() + AddOrder.Num = utility.SafeDiv(buyPrice, fromString).Truncate(int32(tradeSet.AmountDigit)).String() } if utility.StringToFloat64(AddOrder.Num) < tradeSet.MinQty { *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 小于最小下单数量", id, req.Symbol)) @@ -534,11 +534,14 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP StopLossRatio: req.ReduceStopLossRatio, } mainPrice := utility.StringToDecimal(AddOrder.Price) - mainAmount := buyPrice.Div(mainPrice) + mainAmount := utility.SafeDiv(buyPrice, mainPrice) defultExt.TotalAfter = utility.StrToDecimal(AddOrder.Num).Truncate(int32(tradeSet.AmountDigit)) defultExt2.TotalBefore = defultExt.TotalAfter - defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) - defultExt2.ReTakeRatio = req.ReducePriceRatio.Div(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(2) + + // if decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Cmp(decimal.Zero) > 0 { + defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(utility.SafeDiv(req.ReduceNumRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.AmountDigit)) + defultExt2.ReTakeRatio = utility.SafeDiv(req.ReducePriceRatio, utility.SafeDiv(decimal.NewFromInt(100).Sub(req.ReduceNumRatio), decimal.NewFromInt(100))).Truncate(2) + preOrderExts = append(preOrderExts, defultExt) preOrderExts = append(preOrderExts, defultExt2) @@ -644,9 +647,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP profitOrder.MainId = AddOrder.Id if req.ProfitNumRatio.Cmp(decimal.Zero) > 0 { - numPercent := req.ProfitNumRatio.Div(decimal.NewFromInt(100)) + numPercent := utility.SafeDiv(req.ProfitNumRatio, decimal.NewFromInt(100)) profitOrder.Num = utility.StrToDecimal(profitOrder.Num).Mul(numPercent).Truncate(int32(tradeSet.AmountDigit)).String() - } tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&profitOrder) @@ -668,10 +670,10 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP if req.ReducePriceRatio.Cmp(decimal.Zero) > 0 { if strings.ToUpper(req.Site) == "BUY" { stopOrder.Site = "SELL" - stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Sub(req.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() } else { stopOrder.Site = "BUY" - stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(req.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() } stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) stopOrder.Pid = AddOrder.Id @@ -807,12 +809,12 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx data.Price = dataPrice.String() if v.AddPositionType == 1 { - buyPrice := utility.StrToDecimal(preOrder.BuyPrice).Mul(v.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(2) - data.Num = buyPrice.Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() + buyPrice := utility.StrToDecimal(preOrder.BuyPrice).Mul(utility.SafeDiv(v.AddPositionVal, decimal.NewFromInt(100))).Truncate(2) + data.Num = utility.SafeDiv(buyPrice, dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() data.BuyPrice = buyPrice.String() } else { data.BuyPrice = v.AddPositionVal.Truncate(2).String() - data.Num = v.AddPositionVal.Truncate(2).Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() + data.Num = utility.SafeDiv(v.AddPositionVal.Truncate(2), dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() } return data @@ -886,7 +888,7 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO if ext.TakeProfitNumRatio.Cmp(decimal.Zero) <= 0 || ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) >= 0 { profitOrder.Num = ext.TotalAfter.Truncate(int32(tradeSet.AmountDigit)).String() } else { - profitOrder.Num = ext.TotalAfter.Mul(ext.TakeProfitNumRatio).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)).String() + profitOrder.Num = ext.TotalAfter.Mul(utility.SafeDiv(ext.TakeProfitNumRatio, decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() } // 止盈需要累加之前的亏损 @@ -969,7 +971,7 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line } if ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { - percent := decimal.NewFromInt(1).Sub(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))) + percent := decimal.NewFromInt(1).Sub(utility.SafeDiv(ext.TakeProfitNumRatio, decimal.NewFromInt(100))) num = ext.TotalAfter.Mul(percent).Truncate(int32(tradeSet.AmountDigit)) } @@ -1473,7 +1475,7 @@ func (e *LinePreOrder) GetFutSpotOrderInfo(req dto.ManuallyCover, symbol, orderT func (e *LinePreOrder) CalculateAmount(req dto.ManuallyCover, totalNum, lastPrice decimal.Decimal, amountDigit int, notUsdt bool, symbolInfo models.LineSymbol) (decimal.Decimal, error) { var amt decimal.Decimal if req.CoverType == 1 { - decimalValue := utility.StringToDecimal(req.Value).Div(decimal.NewFromInt(100)) + decimalValue := utility.SafeDiv(utility.StringToDecimal(req.Value), decimal.NewFromInt(100)) amt = totalNum.Mul(decimalValue) } else { decimalValue := utility.StringToDecimal(req.Value) @@ -1495,8 +1497,8 @@ func (e *LinePreOrder) CalculateAmount(req dto.ManuallyCover, totalNum, lastPric for _, symbolMap := range tickerSymbolMaps { if symbolMap.Symbol == strings.ToUpper(symbolInfo.QuoteAsset+"USDT") { uTickerPrice, _ := decimal.NewFromString(symbolMap.Price) - div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice)) - amt = decimalValue.Div(div) + div := utility.SafeDiv(tickerPrice, decimal.NewFromInt(1).Div(uTickerPrice)) + amt = utility.SafeDiv(decimalValue, div) break } } @@ -1571,7 +1573,7 @@ func (e *LinePreOrder) SpotClosePosition(position *dto.ClosePosition, errs *[]er "newClientOrderId": utility.Int64ToString(snowflakehelper.GetOrderId()), "timeInForce": "GTC", } - price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) + price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) paramsMaps["price"] = price.String() } else { *errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 下单数量小于最小下单数量", position.ApiId))) @@ -1630,9 +1632,27 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err //查询已经开仓的合约交易对 var futList []models.LinePreOrder if position.Symbol == "" { - e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND status = 6 AND symbol_type =2 AND order_type =0 AND main_id = 0", position.ApiId).Find(&futList) + query := e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND status = 6 AND symbol_type =2 AND order_type =0 AND main_id = 0", position.ApiId) + + switch strings.ToUpper(position.Side) { + case "BUY": + query = query.Where("site = 'BUY'") + case "SELL": + query = query.Where("site = 'SELL'") + } + + query.Find(&futList) } else { - e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND symbol_type =2 AND status = 6 AND order_type = 0 AND main_id = 0", position.ApiId, position.Symbol).Find(&futList) + query := e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND symbol_type =2 AND status = 6 AND order_type = 0 AND main_id = 0", position.ApiId, position.Symbol) + + switch strings.ToUpper(position.Side) { + case "BUY": + query = query.Where("side = 'BUY'") + case "SELL": + query = query.Where("side = 'SELL'") + } + + query.Find(&futList) } if len(futList) <= 0 { *errs = append(*errs, fmt.Errorf("api_id:%d 没有可平仓的交易对", position.ApiId)) @@ -1677,10 +1697,10 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err if list.Site == "BUY" && risk.PositionSide == "LONG" { //做多 //根据仓位数量去平多 orderSide = "SELL" - price = lastPrice.Mul(decimal.NewFromInt(1).Add(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) + price = lastPrice.Mul(decimal.NewFromInt(1).Add(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) } else if list.Site == "SELL" && risk.PositionSide == "SHORT" { orderSide = "BUY" - price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) + price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) } if price.LessThanOrEqual(decimal.Zero) { @@ -1726,6 +1746,9 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err //撤销合约的委托(根据方向撤) orderSns, _ := binanceservice.GetOpenOrderSns(e.Orm, []int{list.Id}) + if len(orderSns) > 0 { + logger.Infof("平仓 取消 订单id :%v 订单号: %v", orderSns) + } api.CancelBatchFutOrder(apiUserInfo, list.Symbol, orderSns) // api.CancelAllFutOrder(apiUserInfo, list.Symbol) //side=BUY&positionSide=LONG是开多, diff --git a/pkg/utility/decimalhelper.go b/pkg/utility/decimalhelper.go index 93f75d8..0fb9831 100644 --- a/pkg/utility/decimalhelper.go +++ b/pkg/utility/decimalhelper.go @@ -87,3 +87,16 @@ func DiscardDecimal(value decimal.Decimal, discardDigits int32) decimal.Decimal return value } + +// SafeDiv 安全除法 +// dividend: 被除数 +// divisor: 除数 +func SafeDiv(dividend, divisor decimal.Decimal) decimal.Decimal { + var result decimal.Decimal + + if dividend.Cmp(decimal.Zero) != 0 && divisor.Cmp(decimal.Zero) != 0 { + result = dividend.Div(divisor) + } + + return result +} diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 0d2ce5b..6855131 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -303,12 +303,12 @@ func handleClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) removePosition(db, preOrder) - futApi := FutRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) if apiUserInfo.Id > 0 { - if err := futApi.CancelAllFutOrder(apiUserInfo, preOrder.Symbol); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + mainIds := []int{preOrder.MainId} + if err := cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil { + logger.Errorf("平仓单成功 取消其它订单失败 订单号:%s:", err) } } diff --git a/services/scriptservice/order.go b/services/scriptservice/order.go index 0b8edd5..4a78831 100644 --- a/services/scriptservice/order.go +++ b/services/scriptservice/order.go @@ -1,17 +1,19 @@ package scriptservice import ( - "github.com/bytedance/sonic" - log "github.com/go-admin-team/go-admin-core/logger" - sysservice "github.com/go-admin-team/go-admin-core/sdk/service" "go-admin/app/admin/models" "go-admin/app/admin/service" "go-admin/app/admin/service/dto" "go-admin/common/const/rediskey" "go-admin/common/helper" - "gorm.io/gorm" + "go-admin/pkg/utility" "strings" "sync" + + "github.com/bytedance/sonic" + log "github.com/go-admin-team/go-admin-core/logger" + sysservice "github.com/go-admin-team/go-admin-core/sdk/service" + "gorm.io/gorm" ) type PreOrder struct { @@ -23,8 +25,9 @@ func (receiver *PreOrder) AddOrder(orm *gorm.DB) { var wg sync.WaitGroup for i := 1; i <= GoroutineNum; i++ { wg.Add(1) - go workerWithLock(orm, &wg) - + utility.SafeGo(func() { + workerWithLock(orm, &wg) + }) } wg.Wait() }