This commit is contained in:
2025-05-15 18:39:19 +08:00
parent 8e2324df0e
commit 38a5acface
35 changed files with 3493 additions and 385 deletions

View File

@ -0,0 +1,194 @@
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 WmNetwork struct {
api.Api
}
// GetPage 获取网络配置列表
// @Summary 获取网络配置列表
// @Description 获取网络配置列表
// @Tags 网络配置
// @Param networkName query string false "网络名称"
// @Param code query string false "网络代码"
// @Param receiveAddress query string false "接收钱包地址"
// @Param pageSize query int false "页条数"
// @Param pageIndex query int false "页码"
// @Success 200 {object} response.Response{data=response.Page{list=[]models.WmNetwork}} "{"code": 200, "data": [...]}"
// @Router /api/v1/wm-network [get]
// @Security Bearer
func (e WmNetwork) GetPage(c *gin.Context) {
req := dto.WmNetworkGetPageReq{}
s := service.WmNetwork{}
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.WmNetwork, 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.WmNetwork} "{"code": 200, "data": [...]}"
// @Router /api/v1/wm-network/{id} [get]
// @Security Bearer
func (e WmNetwork) Get(c *gin.Context) {
req := dto.WmNetworkGetReq{}
s := service.WmNetwork{}
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.WmNetwork
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.WmNetworkInsertReq true "data"
// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
// @Router /api/v1/wm-network [post]
// @Security Bearer
// func (e WmNetwork) Insert(c *gin.Context) {
// req := dto.WmNetworkInsertReq{}
// s := service.WmNetwork{}
// 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.WmNetworkUpdateReq true "body"
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
// @Router /api/v1/wm-network/{id} [put]
// @Security Bearer
func (e WmNetwork) Update(c *gin.Context) {
req := dto.WmNetworkUpdateReq{}
s := service.WmNetwork{}
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.WmNetworkDeleteReq true "body"
// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
// @Router /api/v1/wm-network [delete]
// @Security Bearer
// func (e WmNetwork) Delete(c *gin.Context) {
// s := service.WmNetwork{}
// req := dto.WmNetworkDeleteReq{}
// 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(), "删除成功")
// }

205
app/admin/apis/wm_token.go Normal file
View File

@ -0,0 +1,205 @@
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 WmToken struct {
api.Api
}
// GetPage 获取代币配置列表
// @Summary 获取代币配置列表
// @Description 获取代币配置列表
// @Tags 代币配置
// @Param networkId query string false "网络id"
// @Param tokenName query string false "代币名称"
// @Param pageSize query int false "页条数"
// @Param pageIndex query int false "页码"
// @Success 200 {object} response.Response{data=response.Page{list=[]models.WmToken}} "{"code": 200, "data": [...]}"
// @Router /api/v1/wm-token [get]
// @Security Bearer
func (e WmToken) GetPage(c *gin.Context) {
req := dto.WmTokenGetPageReq{}
s := service.WmToken{}
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.WmToken, 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.WmToken} "{"code": 200, "data": [...]}"
// @Router /api/v1/wm-token/{id} [get]
// @Security Bearer
func (e WmToken) Get(c *gin.Context) {
req := dto.WmTokenGetReq{}
s := service.WmToken{}
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.WmToken
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.WmTokenInsertReq true "data"
// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
// @Router /api/v1/wm-token [post]
// @Security Bearer
func (e WmToken) Insert(c *gin.Context) {
req := dto.WmTokenInsertReq{}
s := service.WmToken{}
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
}
// 设置创建人
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.WmTokenUpdateReq true "body"
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
// @Router /api/v1/wm-token/{id} [put]
// @Security Bearer
func (e WmToken) Update(c *gin.Context) {
req := dto.WmTokenUpdateReq{}
s := service.WmToken{}
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
}
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.WmTokenDeleteReq true "body"
// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
// @Router /api/v1/wm-token [delete]
// @Security Bearer
func (e WmToken) Delete(c *gin.Context) {
s := service.WmToken{}
req := dto.WmTokenDeleteReq{}
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(), "删除成功")
}

View File

@ -1,9 +1,10 @@
package apis package apis
import ( import (
"fmt" "fmt"
"github.com/gin-gonic/gin" "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/api"
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user" "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
_ "github.com/go-admin-team/go-admin-core/sdk/pkg/response" _ "github.com/go-admin-team/go-admin-core/sdk/pkg/response"
@ -30,18 +31,18 @@ type WmTransfer struct {
// @Router /api/v1/wm-transfer [get] // @Router /api/v1/wm-transfer [get]
// @Security Bearer // @Security Bearer
func (e WmTransfer) GetPage(c *gin.Context) { func (e WmTransfer) GetPage(c *gin.Context) {
req := dto.WmTransferGetPageReq{} req := dto.WmTransferGetPageReq{}
s := service.WmTransfer{} s := service.WmTransfer{}
err := e.MakeContext(c). err := e.MakeContext(c).
MakeOrm(). MakeOrm().
Bind(&req). Bind(&req, binding.Form).
MakeService(&s.Service). MakeService(&s.Service).
Errors Errors
if err != nil { if err != nil {
e.Logger.Error(err) e.Logger.Error(err)
e.Error(500, err, err.Error()) e.Error(500, err, err.Error())
return return
} }
p := actions.GetPermissionFromContext(c) p := actions.GetPermissionFromContext(c)
list := make([]models.WmTransfer, 0) list := make([]models.WmTransfer, 0)
@ -50,7 +51,7 @@ func (e WmTransfer) GetPage(c *gin.Context) {
err = s.GetPage(&req, p, &list, &count) err = s.GetPage(&req, p, &list, &count)
if err != nil { if err != nil {
e.Error(500, err, fmt.Sprintf("获取批量转账任务失败,\r\n失败信息 %s", err.Error())) e.Error(500, err, fmt.Sprintf("获取批量转账任务失败,\r\n失败信息 %s", err.Error()))
return return
} }
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
@ -67,7 +68,7 @@ func (e WmTransfer) GetPage(c *gin.Context) {
func (e WmTransfer) Get(c *gin.Context) { func (e WmTransfer) Get(c *gin.Context) {
req := dto.WmTransferGetReq{} req := dto.WmTransferGetReq{}
s := service.WmTransfer{} s := service.WmTransfer{}
err := e.MakeContext(c). err := e.MakeContext(c).
MakeOrm(). MakeOrm().
Bind(&req). Bind(&req).
MakeService(&s.Service). MakeService(&s.Service).
@ -83,10 +84,10 @@ func (e WmTransfer) Get(c *gin.Context) {
err = s.Get(&req, p, &object) err = s.Get(&req, p, &object)
if err != nil { if err != nil {
e.Error(500, err, fmt.Sprintf("获取批量转账任务失败,\r\n失败信息 %s", err.Error())) e.Error(500, err, fmt.Sprintf("获取批量转账任务失败,\r\n失败信息 %s", err.Error()))
return return
} }
e.OK( object, "查询成功") e.OK(object, "查询成功")
} }
// Insert 创建批量转账任务 // Insert 创建批量转账任务
@ -100,65 +101,36 @@ func (e WmTransfer) Get(c *gin.Context) {
// @Router /api/v1/wm-transfer [post] // @Router /api/v1/wm-transfer [post]
// @Security Bearer // @Security Bearer
func (e WmTransfer) Insert(c *gin.Context) { func (e WmTransfer) Insert(c *gin.Context) {
req := dto.WmTransferInsertReq{} req := dto.WmTransferInsertReq{}
s := service.WmTransfer{} s := service.WmTransfer{}
err := e.MakeContext(c). err := e.MakeContext(c).
MakeOrm(). MakeOrm().
Bind(&req). Bind(&req).
MakeService(&s.Service). MakeService(&s.Service).
Errors Errors
if err != nil { if err != nil {
e.Logger.Error(err) e.Logger.Error(err)
e.Error(500, err, err.Error()) e.Error(500, err, err.Error())
return return
} }
if err := req.Valid(); err != nil {
e.Error(500, err, err.Error())
return
}
// 设置创建人 // 设置创建人
req.SetCreateBy(user.GetUserId(c)) req.SetCreateBy(user.GetUserId(c))
err = s.Insert(&req) err = s.Insert(&req)
if err != nil { if err != nil {
e.Error(500, err, fmt.Sprintf("创建批量转账任务失败,\r\n失败信息 %s", err.Error())) e.Error(500, err, fmt.Sprintf("创建批量转账任务失败,\r\n失败信息 %s", err.Error()))
return return
} }
e.OK(req.GetId(), "创建成功") e.OK(req.GetId(), "创建成功")
} }
// Update 修改批量转账任务
// @Summary 修改批量转账任务
// @Description 修改批量转账任务
// @Tags 批量转账任务
// @Accept application/json
// @Product application/json
// @Param id path int true "id"
// @Param data body dto.WmTransferUpdateReq true "body"
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
// @Router /api/v1/wm-transfer/{id} [put]
// @Security Bearer
func (e WmTransfer) Update(c *gin.Context) {
req := dto.WmTransferUpdateReq{}
s := service.WmTransfer{}
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 删除批量转账任务 // Delete 删除批量转账任务
// @Summary 删除批量转账任务 // @Summary 删除批量转账任务
// @Description 删除批量转账任务 // @Description 删除批量转账任务
@ -168,18 +140,18 @@ func (e WmTransfer) Update(c *gin.Context) {
// @Router /api/v1/wm-transfer [delete] // @Router /api/v1/wm-transfer [delete]
// @Security Bearer // @Security Bearer
func (e WmTransfer) Delete(c *gin.Context) { func (e WmTransfer) Delete(c *gin.Context) {
s := service.WmTransfer{} s := service.WmTransfer{}
req := dto.WmTransferDeleteReq{} req := dto.WmTransferDeleteReq{}
err := e.MakeContext(c). err := e.MakeContext(c).
MakeOrm(). MakeOrm().
Bind(&req). Bind(&req).
MakeService(&s.Service). MakeService(&s.Service).
Errors Errors
if err != nil { if err != nil {
e.Logger.Error(err) e.Logger.Error(err)
e.Error(500, err, err.Error()) e.Error(500, err, err.Error())
return return
} }
// req.SetUpdateBy(user.GetUserId(c)) // req.SetUpdateBy(user.GetUserId(c))
p := actions.GetPermissionFromContext(c) p := actions.GetPermissionFromContext(c)
@ -187,7 +159,28 @@ func (e WmTransfer) Delete(c *gin.Context) {
err = s.Remove(&req, p) err = s.Remove(&req, p)
if err != nil { if err != nil {
e.Error(500, err, fmt.Sprintf("删除批量转账任务失败,\r\n失败信息 %s", err.Error())) e.Error(500, err, fmt.Sprintf("删除批量转账任务失败,\r\n失败信息 %s", err.Error()))
return return
} }
e.OK( req.GetId(), "删除成功") e.OK(req.GetId(), "删除成功")
}
// 清理数据
func (e WmTransfer) ClearAll(c *gin.Context) {
s := service.WmTransfer{}
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.ClearAll()
if err != nil {
e.Error(500, err, fmt.Sprintf("清理数据失败,\r\n失败信息 %s", err.Error()))
return
}
e.OK(nil, "清理数据成功")
} }

View File

@ -1,9 +1,10 @@
package apis package apis
import ( import (
"fmt" "fmt"
"github.com/gin-gonic/gin" "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/api"
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user" "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
_ "github.com/go-admin-team/go-admin-core/sdk/pkg/response" _ "github.com/go-admin-team/go-admin-core/sdk/pkg/response"
@ -28,18 +29,18 @@ type WmTransferItem struct {
// @Router /api/v1/wm-transfer-item [get] // @Router /api/v1/wm-transfer-item [get]
// @Security Bearer // @Security Bearer
func (e WmTransferItem) GetPage(c *gin.Context) { func (e WmTransferItem) GetPage(c *gin.Context) {
req := dto.WmTransferItemGetPageReq{} req := dto.WmTransferItemGetPageReq{}
s := service.WmTransferItem{} s := service.WmTransferItem{}
err := e.MakeContext(c). err := e.MakeContext(c).
MakeOrm(). MakeOrm().
Bind(&req). Bind(&req).
MakeService(&s.Service). MakeService(&s.Service).
Errors Errors
if err != nil { if err != nil {
e.Logger.Error(err) e.Logger.Error(err)
e.Error(500, err, err.Error()) e.Error(500, err, err.Error())
return return
} }
p := actions.GetPermissionFromContext(c) p := actions.GetPermissionFromContext(c)
list := make([]models.WmTransferItem, 0) list := make([]models.WmTransferItem, 0)
@ -48,12 +49,59 @@ func (e WmTransferItem) GetPage(c *gin.Context) {
err = s.GetPage(&req, p, &list, &count) err = s.GetPage(&req, p, &list, &count)
if err != nil { if err != nil {
e.Error(500, err, fmt.Sprintf("获取批量转账明细失败,\r\n失败信息 %s", err.Error())) e.Error(500, err, fmt.Sprintf("获取批量转账明细失败,\r\n失败信息 %s", err.Error()))
return return
} }
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
} }
// 导出excel
func (e WmTransferItem) Export(c *gin.Context) {
s := service.WmTransferItem{}
req := dto.WmTransferItemExportReq{}
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)
err = s.ExportExcel(&req, p, c)
if err != nil {
e.Error(500, err, fmt.Sprintf("导出excel失败\r\n失败信息 %s", err.Error()))
return
}
}
// ExportAutoLog 导出自动转账日志
func (e WmTransferItem) ExportAutoLog(c *gin.Context) {
s := service.WmTransferItem{}
req := dto.WmTransferItemAutoLogPageReq{}
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
}
err = s.ExportAutoLog(&req, c)
if err != nil {
e.Error(500, err, fmt.Sprintf("导出自动转账日志失败,\r\n失败信息 %s", err.Error()))
return
}
}
// Get 获取批量转账明细 // Get 获取批量转账明细
// @Summary 获取批量转账明细 // @Summary 获取批量转账明细
// @Description 获取批量转账明细 // @Description 获取批量转账明细
@ -65,7 +113,7 @@ func (e WmTransferItem) GetPage(c *gin.Context) {
func (e WmTransferItem) Get(c *gin.Context) { func (e WmTransferItem) Get(c *gin.Context) {
req := dto.WmTransferItemGetReq{} req := dto.WmTransferItemGetReq{}
s := service.WmTransferItem{} s := service.WmTransferItem{}
err := e.MakeContext(c). err := e.MakeContext(c).
MakeOrm(). MakeOrm().
Bind(&req). Bind(&req).
MakeService(&s.Service). MakeService(&s.Service).
@ -81,10 +129,10 @@ func (e WmTransferItem) Get(c *gin.Context) {
err = s.Get(&req, p, &object) err = s.Get(&req, p, &object)
if err != nil { if err != nil {
e.Error(500, err, fmt.Sprintf("获取批量转账明细失败,\r\n失败信息 %s", err.Error())) e.Error(500, err, fmt.Sprintf("获取批量转账明细失败,\r\n失败信息 %s", err.Error()))
return return
} }
e.OK( object, "查询成功") e.OK(object, "查询成功")
} }
// Insert 创建批量转账明细 // Insert 创建批量转账明细
@ -98,25 +146,25 @@ func (e WmTransferItem) Get(c *gin.Context) {
// @Router /api/v1/wm-transfer-item [post] // @Router /api/v1/wm-transfer-item [post]
// @Security Bearer // @Security Bearer
func (e WmTransferItem) Insert(c *gin.Context) { func (e WmTransferItem) Insert(c *gin.Context) {
req := dto.WmTransferItemInsertReq{} req := dto.WmTransferItemInsertReq{}
s := service.WmTransferItem{} s := service.WmTransferItem{}
err := e.MakeContext(c). err := e.MakeContext(c).
MakeOrm(). MakeOrm().
Bind(&req). Bind(&req).
MakeService(&s.Service). MakeService(&s.Service).
Errors Errors
if err != nil { if err != nil {
e.Logger.Error(err) e.Logger.Error(err)
e.Error(500, err, err.Error()) e.Error(500, err, err.Error())
return return
} }
// 设置创建人 // 设置创建人
req.SetCreateBy(user.GetUserId(c)) req.SetCreateBy(user.GetUserId(c))
err = s.Insert(&req) err = s.Insert(&req)
if err != nil { if err != nil {
e.Error(500, err, fmt.Sprintf("创建批量转账明细失败,\r\n失败信息 %s", err.Error())) e.Error(500, err, fmt.Sprintf("创建批量转账明细失败,\r\n失败信息 %s", err.Error()))
return return
} }
e.OK(req.GetId(), "创建成功") e.OK(req.GetId(), "创建成功")
@ -134,27 +182,27 @@ func (e WmTransferItem) Insert(c *gin.Context) {
// @Router /api/v1/wm-transfer-item/{id} [put] // @Router /api/v1/wm-transfer-item/{id} [put]
// @Security Bearer // @Security Bearer
func (e WmTransferItem) Update(c *gin.Context) { func (e WmTransferItem) Update(c *gin.Context) {
req := dto.WmTransferItemUpdateReq{} req := dto.WmTransferItemUpdateReq{}
s := service.WmTransferItem{} s := service.WmTransferItem{}
err := e.MakeContext(c). err := e.MakeContext(c).
MakeOrm(). MakeOrm().
Bind(&req). Bind(&req).
MakeService(&s.Service). MakeService(&s.Service).
Errors Errors
if err != nil { if err != nil {
e.Logger.Error(err) e.Logger.Error(err)
e.Error(500, err, err.Error()) e.Error(500, err, err.Error())
return return
} }
req.SetUpdateBy(user.GetUserId(c)) req.SetUpdateBy(user.GetUserId(c))
p := actions.GetPermissionFromContext(c) p := actions.GetPermissionFromContext(c)
err = s.Update(&req, p) err = s.Update(&req, p)
if err != nil { if err != nil {
e.Error(500, err, fmt.Sprintf("修改批量转账明细失败,\r\n失败信息 %s", err.Error())) e.Error(500, err, fmt.Sprintf("修改批量转账明细失败,\r\n失败信息 %s", err.Error()))
return return
} }
e.OK( req.GetId(), "修改成功") e.OK(req.GetId(), "修改成功")
} }
// Delete 删除批量转账明细 // Delete 删除批量转账明细
@ -166,18 +214,18 @@ func (e WmTransferItem) Update(c *gin.Context) {
// @Router /api/v1/wm-transfer-item [delete] // @Router /api/v1/wm-transfer-item [delete]
// @Security Bearer // @Security Bearer
func (e WmTransferItem) Delete(c *gin.Context) { func (e WmTransferItem) Delete(c *gin.Context) {
s := service.WmTransferItem{} s := service.WmTransferItem{}
req := dto.WmTransferItemDeleteReq{} req := dto.WmTransferItemDeleteReq{}
err := e.MakeContext(c). err := e.MakeContext(c).
MakeOrm(). MakeOrm().
Bind(&req). Bind(&req).
MakeService(&s.Service). MakeService(&s.Service).
Errors Errors
if err != nil { if err != nil {
e.Logger.Error(err) e.Logger.Error(err)
e.Error(500, err, err.Error()) e.Error(500, err, err.Error())
return return
} }
// req.SetUpdateBy(user.GetUserId(c)) // req.SetUpdateBy(user.GetUserId(c))
p := actions.GetPermissionFromContext(c) p := actions.GetPermissionFromContext(c)
@ -185,7 +233,32 @@ func (e WmTransferItem) Delete(c *gin.Context) {
err = s.Remove(&req, p) err = s.Remove(&req, p)
if err != nil { if err != nil {
e.Error(500, err, fmt.Sprintf("删除批量转账明细失败,\r\n失败信息 %s", err.Error())) e.Error(500, err, fmt.Sprintf("删除批量转账明细失败,\r\n失败信息 %s", err.Error()))
return return
} }
e.OK( req.GetId(), "删除成功") e.OK(req.GetId(), "删除成功")
}
// GetAutoTransferLogPage 获取自动转账日志列表
func (e WmTransferItem) GetAutoTransferLogPage(c *gin.Context) {
req := dto.WmTransferItemAutoLogPageReq{}
s := service.WmTransferItem{}
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([]dto.WmTransferItemResp, 0)
var count int64
err = s.GetAutoTransferLogPage(&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(), "查询成功")
} }

View File

@ -152,3 +152,25 @@ func (e WmWalletInfo) Delete(c *gin.Context) {
} }
e.OK(req.GetId(), "删除成功") e.OK(req.GetId(), "删除成功")
} }
// ClearAll 清空所有钱包信息
func (e WmWalletInfo) ClearAll(c *gin.Context) {
s := service.WmWalletInfo{}
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.ClearAll()
if err != nil {
e.Error(500, err, fmt.Sprintf("清空所有钱包信息失败,\r\n失败信息 %s", err.Error()))
return
}
e.OK(nil, "清空成功")
}

View File

@ -0,0 +1,28 @@
package models
import (
"go-admin/common/models"
)
type WmNetwork struct {
models.Model
NetworkName string `json:"networkName" gorm:"type:varchar(20);comment:网络名称"`
Code string `json:"code" gorm:"type:varchar(50);comment:网络代码"`
ReceiveAddress string `json:"receiveAddress" gorm:"type:varchar(255);comment:接收钱包地址"`
models.ModelTime
models.ControlBy
}
func (WmNetwork) TableName() string {
return "wm_network"
}
func (e *WmNetwork) Generate() models.ActiveRecord {
o := *e
return &o
}
func (e *WmNetwork) GetId() interface{} {
return e.Id
}

View File

@ -0,0 +1,36 @@
package models
import (
"go-admin/common/models"
"github.com/shopspring/decimal"
)
type WmToken struct {
models.Model
NetworkId int `json:"networkId" gorm:"type:bigint;comment:网络id"`
NetworkCode string `json:"code" gorm:"-"`
TokenName string `json:"tokenName" gorm:"type:varchar(20);comment:代币名称"`
TokenAddress string `json:"tokenAddress" gorm:"type:varchar(50);comment:代币地址"`
Decimals int `json:"decimals" gorm:"type:int;comment:代币精度"`
IsAuto int `json:"isAuto" gorm:"type:tinyint;comment:开启自动转账 1-开启 2-关闭"`
TriggerAmount decimal.Decimal `json:"triggerAmount" gorm:"type:decimal(18,8);comment:触发数量"`
TransType int `json:"transType" gorm:"type:tinyint;comment:转账类型 1-百分比 2-实际金额"`
TransValue decimal.Decimal `json:"transValue" gorm:"type:decimal(18,8);comment:转账值"`
models.ModelTime
models.ControlBy
}
func (WmToken) TableName() string {
return "wm_token"
}
func (e *WmToken) Generate() models.ActiveRecord {
o := *e
return &o
}
func (e *WmToken) GetId() interface{} {
return e.Id
}

View File

@ -1,24 +1,28 @@
package models package models
import ( import (
"go-admin/common/models" "go-admin/common/models"
) )
type WmTransfer struct { type WmTransfer struct {
models.Model models.Model
Type int64 `json:"type" gorm:"type:tinyint;comment:类型 0-百分比 1-实际金额"` NetworkId int `json:"networkId" gorm:"type:int;comment:网络id"`
TransferType int64 `json:"transferType" gorm:"type:tinyint;comment:转账类型 0-批量转出 1-批量汇总"` TokenAddress string `json:"tokenAddress" gorm:"type:varchar(255);comment:代币地址"`
Status int64 `json:"status" gorm:"type:tinyint;comment:状态"` Type int `json:"type" gorm:"type:tinyint;comment:类型 0-百分比 1-实际金额"`
Remark string `json:"remark" gorm:"type:varchar(255);comment:备注信息"` TransferType int `json:"transferType" gorm:"type:tinyint;comment:转账类型 0-批量转出 1-批量汇总"`
models.ModelTime Status int `json:"status" gorm:"type:tinyint;comment:状态"`
models.ControlBy Remark string `json:"remark" gorm:"type:varchar(255);comment:备注信息"`
Total int `json:"total" gorm:"-"`
Sucess int `json:"sucess" gorm:"-"`
Fail int `json:"fail" gorm:"-"`
Pending int `json:"pending" gorm:"-"`
models.ModelTime
models.ControlBy
} }
func (WmTransfer) TableName() string { func (WmTransfer) TableName() string {
return "wm_transfer" return "wm_transfer"
} }
func (e *WmTransfer) Generate() models.ActiveRecord { func (e *WmTransfer) Generate() models.ActiveRecord {
@ -28,4 +32,4 @@ func (e *WmTransfer) Generate() models.ActiveRecord {
func (e *WmTransfer) GetId() interface{} { func (e *WmTransfer) GetId() interface{} {
return e.Id return e.Id
} }

View File

@ -1,27 +1,34 @@
package models package models
import ( import (
"go-admin/common/models" "go-admin/common/models"
"github.com/shopspring/decimal"
) )
type WmTransferItem struct { type WmTransferItem struct {
models.Model models.Model
TokenAddress string `json:"tokenAddress" gorm:"type:varchar(50);comment:代币地址"` NetworkId int `json:"networkId" gorm:"type:int;comment:网络ID"`
FromAddress string `json:"fromAddress" gorm:"type:varchar(50);comment:来源地址"` TransferId int `json:"transferId" gorm:"type:int;comment:转账记录ID"`
ToAddress string `json:"toAddress" gorm:"type:varchar(50);comment:目标地址"` IsAuto int `json:"isAuto" gorm:"type:tinyint;comment:是否自动转账 1-是 2-否 "`
Amount string `json:"amount" gorm:"type:decimal(18,8);comment:代币数量"` TokenAddress string `json:"tokenAddress" gorm:"type:varchar(50);comment:代币地址"`
Type string `json:"type" gorm:"type:tinyint;comment:类型 0-主账号百分比 1-实际数量"` Decimals int `json:"decimals" gorm:"type:int;comment:代币精度"`
TypeValue string `json:"typeValue" gorm:"type:decimal(18,8);comment:操作类型值"` FromAddress string `json:"fromAddress" gorm:"type:varchar(50);comment:来源地址"`
PrivateKey string `json:"privateKey" gorm:"type:varchar(255);comment:私钥"` ToAddress string `json:"toAddress" gorm:"type:varchar(50);comment:目标地址"`
models.ModelTime Amount decimal.Decimal `json:"amount" gorm:"type:decimal(18,8);comment:代币数量"`
models.ControlBy Type int `json:"type" gorm:"type:tinyint;comment:类型 0-主账号百分比 1-实际数量"`
TypeValue decimal.Decimal `json:"typeValue" gorm:"type:decimal(18,8);comment:操作类型值"`
PrivateKey string `json:"privateKey" gorm:"type:varchar(255);comment:私钥"`
Status int `json:"status" gorm:"type:tinyint;comment:状态 0-默认 1-已发起 2-成功 3-失败"`
Hash string `json:"hash" gorm:"type:varchar(255);comment:交易hash"`
Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"`
models.ModelTime
models.ControlBy
} }
func (WmTransferItem) TableName() string { func (WmTransferItem) TableName() string {
return "wm_transfer_item" return "wm_transfer_item"
} }
func (e *WmTransferItem) Generate() models.ActiveRecord { func (e *WmTransferItem) Generate() models.ActiveRecord {
@ -31,4 +38,4 @@ func (e *WmTransferItem) Generate() models.ActiveRecord {
func (e *WmTransferItem) GetId() interface{} { func (e *WmTransferItem) GetId() interface{} {
return e.Id return e.Id
} }

View File

@ -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/actions"
"go-admin/common/middleware"
)
func init() {
routerCheckRole = append(routerCheckRole, registerWmNetworkRouter)
}
// registerWmNetworkRouter
func registerWmNetworkRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
api := apis.WmNetwork{}
r := v1.Group("/wm-network").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)
}
}

View File

@ -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, registerWmTokenRouter)
}
// registerWmTokenRouter
func registerWmTokenRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
api := apis.WmToken{}
r := v1.Group("/wm-token").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)
}
}

View File

@ -5,8 +5,8 @@ import (
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
"go-admin/app/admin/apis" "go-admin/app/admin/apis"
"go-admin/common/middleware"
"go-admin/common/actions" "go-admin/common/actions"
"go-admin/common/middleware"
) )
func init() { func init() {
@ -21,7 +21,9 @@ func registerWmTransferRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMid
r.GET("", actions.PermissionAction(), api.GetPage) r.GET("", actions.PermissionAction(), api.GetPage)
r.GET("/:id", actions.PermissionAction(), api.Get) r.GET("/:id", actions.PermissionAction(), api.Get)
r.POST("", api.Insert) r.POST("", api.Insert)
r.PUT("/:id", actions.PermissionAction(), api.Update) // r.PUT("/:id", actions.PermissionAction(), api.Update)
r.DELETE("", api.Delete) r.DELETE("", api.Delete)
r.DELETE("clear", api.ClearAll)
} }
} }

View File

@ -5,8 +5,8 @@ import (
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
"go-admin/app/admin/apis" "go-admin/app/admin/apis"
"go-admin/common/middleware"
"go-admin/common/actions" "go-admin/common/actions"
"go-admin/common/middleware"
) )
func init() { func init() {
@ -23,5 +23,9 @@ func registerWmTransferItemRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJW
r.POST("", api.Insert) r.POST("", api.Insert)
r.PUT("/:id", actions.PermissionAction(), api.Update) r.PUT("/:id", actions.PermissionAction(), api.Update)
r.DELETE("", api.Delete) r.DELETE("", api.Delete)
r.GET("/export", actions.PermissionAction(), api.Export)
r.GET("/export-auto-log", actions.PermissionAction(), api.ExportAutoLog)
r.GET("/auto-log", actions.PermissionAction(), api.GetAutoTransferLogPage)
} }
} }

View File

@ -23,5 +23,6 @@ func registerWmWalletInfoRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTM
r.POST("", api.Insert) r.POST("", api.Insert)
// r.PUT("/:id", actions.PermissionAction(), api.Update) // r.PUT("/:id", actions.PermissionAction(), api.Update)
r.DELETE("", api.Delete) r.DELETE("", api.Delete)
r.DELETE("clear", api.ClearAll)
} }
} }

View File

@ -0,0 +1,106 @@
package dto
import (
"errors"
"go-admin/app/admin/models"
"go-admin/common/dto"
common "go-admin/common/models"
"go-admin/utils/ethtransferhelper"
)
type WmNetworkGetPageReq struct {
dto.Pagination `search:"-"`
NetworkName string `form:"networkName" search:"type:contains;column:network_name;table:wm_network" comment:"网络名称"`
Code string `form:"code" search:"type:exact;column:code;table:wm_network" comment:"网络代码"`
ReceiveAddress string `form:"receiveAddress" search:"type:exact;column:receive_address;table:wm_network" comment:"接收钱包地址"`
WmNetworkOrder
}
type WmNetworkOrder struct {
Id string `form:"idOrder" search:"type:order;column:id;table:wm_network"`
NetworkName string `form:"networkNameOrder" search:"type:order;column:network_name;table:wm_network"`
Code string `form:"codeOrder" search:"type:order;column:code;table:wm_network"`
ReceiveAddress string `form:"receiveAddressOrder" search:"type:order;column:receive_address;table:wm_network"`
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:wm_network"`
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:wm_network"`
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:wm_network"`
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:wm_network"`
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:wm_network"`
}
func (m *WmNetworkGetPageReq) GetNeedSearch() interface{} {
return *m
}
type WmNetworkInsertReq struct {
Id int `json:"-" comment:"主键id"` // 主键id
NetworkName string `json:"networkName" comment:"网络名称"`
Code string `json:"code" comment:"网络代码"`
ReceiveAddress string `json:"receiveAddress" comment:"接收钱包地址"`
common.ControlBy
}
func (s *WmNetworkInsertReq) Generate(model *models.WmNetwork) {
if s.Id == 0 {
model.Model = common.Model{Id: s.Id}
}
model.NetworkName = s.NetworkName
model.Code = s.Code
model.ReceiveAddress = s.ReceiveAddress
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
}
func (s *WmNetworkInsertReq) GetId() interface{} {
return s.Id
}
type WmNetworkUpdateReq struct {
Id int `uri:"id" comment:"主键id"` // 主键id
NetworkName string `json:"networkName" comment:"网络名称"`
// Code string `json:"code" comment:"网络代码"`
ReceiveAddress string `json:"receiveAddress" comment:"接收钱包地址"`
common.ControlBy
}
func (s *WmNetworkUpdateReq) Valid() error {
if s.NetworkName == "" {
return errors.New("网络名称不能为空")
}
if !ethtransferhelper.IsValidAddress(s.ReceiveAddress) {
return errors.New("接收钱包地址格式不正确")
}
return nil
}
func (s *WmNetworkUpdateReq) Generate(model *models.WmNetwork) {
if s.Id == 0 {
model.Model = common.Model{Id: s.Id}
}
model.NetworkName = s.NetworkName
// model.Code = s.Code
model.ReceiveAddress = s.ReceiveAddress
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
}
func (s *WmNetworkUpdateReq) GetId() interface{} {
return s.Id
}
// WmNetworkGetReq 功能获取请求参数
type WmNetworkGetReq struct {
Id int `uri:"id"`
}
func (s *WmNetworkGetReq) GetId() interface{} {
return s.Id
}
// WmNetworkDeleteReq 功能删除请求参数
type WmNetworkDeleteReq struct {
Ids []int `json:"ids"`
}
func (s *WmNetworkDeleteReq) GetId() interface{} {
return s.Ids
}

View File

@ -0,0 +1,140 @@
package dto
import (
"errors"
"go-admin/app/admin/models"
"go-admin/common/dto"
common "go-admin/common/models"
"github.com/shopspring/decimal"
)
type WmTokenGetPageReq struct {
dto.Pagination `search:"-"`
NetworkId string `form:"networkId" search:"type:contains;column:network_id;table:wm_token" comment:"网络id"`
TokenName string `form:"tokenName" search:"type:contains;column:token_name;table:wm_token" comment:"代币名称"`
WmTokenOrder
}
type WmTokenOrder struct {
Id string `form:"idOrder" search:"type:order;column:id;table:wm_token"`
NetworkId string `form:"networkIdOrder" search:"type:order;column:network_id;table:wm_token"`
TokenName string `form:"tokenNameOrder" search:"type:order;column:token_name;table:wm_token"`
TokenAddress string `form:"tokenAddressOrder" search:"type:order;column:token_address;table:wm_token"`
Decimals string `form:"decimalsOrder" search:"type:order;column:decimals;table:wm_token"`
IsAuto string `form:"isAutoOrder" search:"type:order;column:is_auto;table:wm_token"`
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:wm_token"`
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:wm_token"`
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:wm_token"`
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:wm_token"`
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:wm_token"`
}
func (m *WmTokenGetPageReq) GetNeedSearch() interface{} {
return *m
}
type WmTokenInsertReq struct {
Id int `json:"-" comment:"主键"` // 主键
NetworkId int `json:"networkId" comment:"网络id"`
TokenName string `json:"tokenName" comment:"代币名称"`
TokenAddress string `json:"tokenAddress" comment:"代币地址"`
Decimals int `json:"decimals" comment:"代币精度"`
IsAuto int `json:"isAuto" comment:"开启自动转账 1-开启 2-关闭"`
TriggerAmount decimal.Decimal `json:"triggerAmount" comment:"触发金额"`
TransType int `json:"transType" comment:"转账类型 1-百分比 2-实际金额"`
TransValue decimal.Decimal `json:"transValue" comment:"转账金额"`
common.ControlBy
}
func (s *WmTokenInsertReq) Valid() error {
if s.IsAuto == 1 && (s.TransType == 0 || s.TransValue.IsZero()) {
return errors.New("开启自动转账时,转账类型、转账金额不能为空")
}
if s.IsAuto == 1 && s.TriggerAmount.Cmp(decimal.Zero) <= 0 {
return errors.New("触发金额不能小于0")
}
return nil
}
func (s *WmTokenInsertReq) Generate(model *models.WmToken) {
if s.Id == 0 {
model.Model = common.Model{Id: s.Id}
}
model.NetworkId = s.NetworkId
model.TokenName = s.TokenName
model.TokenAddress = s.TokenAddress
model.Decimals = s.Decimals
model.IsAuto = s.IsAuto
model.TransType = s.TransType
model.TransValue = s.TransValue
model.TriggerAmount = s.TriggerAmount
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
}
func (s *WmTokenInsertReq) GetId() interface{} {
return s.Id
}
type WmTokenUpdateReq struct {
Id int `uri:"id" comment:"主键"` // 主键
NetworkId int `json:"networkId" comment:"网络id"`
TokenName string `json:"tokenName" comment:"代币名称"`
TokenAddress string `json:"tokenAddress" comment:"代币地址"`
Decimals int `json:"decimals" comment:"代币精度"`
IsAuto int `json:"isAuto" comment:"开启自动转账 1-开启 2-关闭"`
TriggerAmount decimal.Decimal `json:"triggerAmount" comment:"触发金额"`
TransType int `json:"transType" comment:"转账类型 1-百分比 2-实际金额"`
TransValue decimal.Decimal `json:"transValue" comment:"转账金额"`
common.ControlBy
}
func (s *WmTokenUpdateReq) Valid() error {
if s.IsAuto == 1 && (s.TransType == 0 || s.TransValue.IsZero()) {
return errors.New("开启自动转账时,转账类型、转账金额不能为空")
}
if s.IsAuto == 1 && s.TriggerAmount.Cmp(decimal.Zero) <= 0 {
return errors.New("触发金额不能小于0")
}
return nil
}
func (s *WmTokenUpdateReq) Generate(model *models.WmToken) {
if s.Id == 0 {
model.Model = common.Model{Id: s.Id}
}
model.NetworkId = s.NetworkId
model.TokenName = s.TokenName
model.TokenAddress = s.TokenAddress
model.Decimals = s.Decimals
model.IsAuto = s.IsAuto
model.TransType = s.TransType
model.TransValue = s.TransValue
model.TriggerAmount = s.TriggerAmount
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
}
func (s *WmTokenUpdateReq) GetId() interface{} {
return s.Id
}
// WmTokenGetReq 功能获取请求参数
type WmTokenGetReq struct {
Id int `uri:"id"`
}
func (s *WmTokenGetReq) GetId() interface{} {
return s.Id
}
// WmTokenDeleteReq 功能删除请求参数
type WmTokenDeleteReq struct {
Ids []int `json:"ids"`
}
func (s *WmTokenDeleteReq) GetId() interface{} {
return s.Ids
}

View File

@ -1,31 +1,33 @@
package dto package dto
import ( import (
"errors"
"go-admin/app/admin/models" "go-admin/app/admin/models"
"go-admin/common/dto" "go-admin/common/dto"
common "go-admin/common/models" common "go-admin/common/models"
"strings"
"github.com/shopspring/decimal"
) )
type WmTransferGetPageReq struct { type WmTransferGetPageReq struct {
dto.Pagination `search:"-"` dto.Pagination `search:"-"`
Type int64 `form:"type" search:"type:exact;column:type;table:wm_transfer" comment:"类型 0-百分比 1-实际金额"` Type int `form:"type" search:"-" comment:"类型 1-百分比 2-实际金额"`
TransferType int64 `form:"transferType" search:"type:exact;column:transfer_type;table:wm_transfer" comment:"转账类型 0-批量转出 1-批量汇总"` TransferType int `form:"transferType" search:"-" comment:"转账类型 1-批量转出 2-批量汇总"`
WmTransferOrder WmTransferOrder
} }
type WmTransferOrder struct { type WmTransferOrder struct {
Id string `form:"idOrder" search:"type:order;column:id;table:wm_transfer"` Id string `form:"idOrder" search:"type:order;column:id;table:wm_transfer"`
Type string `form:"typeOrder" search:"type:order;column:type;table:wm_transfer"` Type string `form:"typeOrder" search:"type:order;column:type;table:wm_transfer"`
TransferType string `form:"transferTypeOrder" search:"type:order;column:transfer_type;table:wm_transfer"` TransferType string `form:"transferTypeOrder" search:"type:order;column:transfer_type;table:wm_transfer"`
Status string `form:"statusOrder" search:"type:order;column:status;table:wm_transfer"` Status string `form:"statusOrder" search:"type:order;column:status;table:wm_transfer"`
Remark string `form:"remarkOrder" search:"type:order;column:remark;table:wm_transfer"` Remark string `form:"remarkOrder" search:"type:order;column:remark;table:wm_transfer"`
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:wm_transfer"` CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:wm_transfer"`
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:wm_transfer"` UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:wm_transfer"`
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:wm_transfer"` CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:wm_transfer"`
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:wm_transfer"` UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:wm_transfer"`
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:wm_transfer"` DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:wm_transfer"`
} }
func (m *WmTransferGetPageReq) GetNeedSearch() interface{} { func (m *WmTransferGetPageReq) GetNeedSearch() interface{} {
@ -33,23 +35,90 @@ func (m *WmTransferGetPageReq) GetNeedSearch() interface{} {
} }
type WmTransferInsertReq struct { type WmTransferInsertReq struct {
Id int `json:"-" comment:"主键id"` // 主键id Id int `json:"-" comment:"主键id"` // 主键id
Type int64 `json:"type" comment:"类型 0-百分比 1-实际金额"` NetworkId int `json:"networkId" comment:"网络id"`
TransferType int64 `json:"transferType" comment:"转账类型 0-批量转出 1-批量汇总"` TokenAddress string `json:"tokenAddress" comment:"代币地址"`
Status int64 `json:"status" comment:"状态"` Type int `json:"type" comment:"类型 1-百分比 2-实际金额"`
Remark string `json:"remark" comment:"备注信息"` TransferType int `json:"transferType" comment:"转账类型 1-批量转出 2-批量汇总"`
common.ControlBy TypeValue decimal.Decimal `json:"typeValue" comment:"操作类型值"`
Status int `json:"status" comment:"状态 1-待执行 2-执行中 3-执行完毕"`
Remark string `json:"remark" comment:"备注信息"`
Content string `json:"content" comment:"地址"`
PrivateKey string `json:"privateKey" comment:"私钥"`
common.ControlBy
} }
func (s *WmTransferInsertReq) Generate(model *models.WmTransfer) { type WmTransferItemCache struct {
if s.Id == 0 { PrivateKey string `json:"privateKey"`
model.Model = common.Model{ Id: s.Id } ToAddress string `json:"toAddress"`
} TokenAddress string `json:"tokenAddress"`
model.Type = s.Type Amount string `json:"amount" comment:"代币数量"`
model.TransferType = s.TransferType Type int `json:"type" comment:"类型 1-百分比 2-实际金额"`
model.Status = s.Status TypeValue decimal.Decimal `json:"typeValue" comment:"操作类型值"`
model.Remark = s.Remark }
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
// Valid 功能验证
func (s *WmTransferInsertReq) Valid() error {
if s.Type > 2 || s.Type < 1 {
return errors.New("类型错误")
}
if s.TransferType > 2 || s.TransferType < 1 {
return errors.New("转账类型错误")
}
s.Content = strings.ReplaceAll(s.Content, " ", "")
s.Content = strings.ReplaceAll(s.Content, ",", "\n")
s.Content = strings.ReplaceAll(s.Content, "\r", "")
s.Content = strings.ReplaceAll(s.Content, "", "\n")
s.PrivateKey = strings.ReplaceAll(s.PrivateKey, " ", "")
s.PrivateKey = strings.ReplaceAll(s.PrivateKey, "\r", "")
s.PrivateKey = strings.ReplaceAll(s.PrivateKey, ",", "\n")
s.PrivateKey = strings.ReplaceAll(s.PrivateKey, "", "\n")
privateKeys := strings.Fields(s.PrivateKey)
s.PrivateKey = strings.Join(privateKeys, "\n")
contents := strings.Fields(s.Content)
s.Content = strings.Join(contents, "\n")
if s.PrivateKey == "" {
return errors.New("私钥不能为空")
}
if s.Content == "" {
return errors.New("地址不能为空")
}
if s.TransferType == 1 {
keys := strings.Fields(s.PrivateKey)
if len(keys) > 1 {
return errors.New("批量转出 只支持单个私钥")
}
} else if s.TransferType == 2 {
keys := strings.Fields(s.Content)
if len(keys) > 1 {
return errors.New("批量汇总 只支持单个地址")
}
}
return nil
}
func (s *WmTransferInsertReq) Generate(model *models.WmTransfer) {
if s.Id == 0 {
model.Model = common.Model{Id: s.Id}
}
model.Type = s.Type
model.TransferType = s.TransferType
model.NetworkId = s.NetworkId
model.TokenAddress = s.TokenAddress
model.Status = 0
model.Remark = s.Remark
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
} }
func (s *WmTransferInsertReq) GetId() interface{} { func (s *WmTransferInsertReq) GetId() interface{} {
@ -57,23 +126,25 @@ func (s *WmTransferInsertReq) GetId() interface{} {
} }
type WmTransferUpdateReq struct { type WmTransferUpdateReq struct {
Id int `uri:"id" comment:"主键id"` // 主键id Id int `uri:"id" comment:"主键id"` // 主键id
Type int64 `json:"type" comment:"类型 0-百分比 1-实际金额"` NetworkId int `json:"networkId" comment:"网络id"`
TransferType int64 `json:"transferType" comment:"转账类型 0-批量转出 1-批量汇总"` TokenAddress string `json:"tokenAddress" comment:"代币地址"`
Status int64 `json:"status" comment:"状态"` Type int `json:"type" comment:"类型 1-百分比 2-实际金额"`
Remark string `json:"remark" comment:"备注信息"` TransferType int `json:"transferType" comment:"转账类型 1-批量转出 2-批量汇总"`
common.ControlBy Status int `json:"status" comment:"状态"`
Remark string `json:"remark" comment:"备注信息"`
common.ControlBy
} }
func (s *WmTransferUpdateReq) Generate(model *models.WmTransfer) { func (s *WmTransferUpdateReq) Generate(model *models.WmTransfer) {
if s.Id == 0 { if s.Id == 0 {
model.Model = common.Model{ Id: s.Id } model.Model = common.Model{Id: s.Id}
} }
model.Type = s.Type model.Type = s.Type
model.TransferType = s.TransferType model.TransferType = s.TransferType
model.Status = s.Status model.Status = s.Status
model.Remark = s.Remark model.Remark = s.Remark
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
} }
func (s *WmTransferUpdateReq) GetId() interface{} { func (s *WmTransferUpdateReq) GetId() interface{} {
@ -82,8 +153,9 @@ func (s *WmTransferUpdateReq) GetId() interface{} {
// WmTransferGetReq 功能获取请求参数 // WmTransferGetReq 功能获取请求参数
type WmTransferGetReq struct { type WmTransferGetReq struct {
Id int `uri:"id"` Id int `uri:"id"`
} }
func (s *WmTransferGetReq) GetId() interface{} { func (s *WmTransferGetReq) GetId() interface{} {
return s.Id return s.Id
} }
@ -96,3 +168,9 @@ type WmTransferDeleteReq struct {
func (s *WmTransferDeleteReq) GetId() interface{} { func (s *WmTransferDeleteReq) GetId() interface{} {
return s.Ids return s.Ids
} }
type TransItemData struct {
TransferId int `json:"transferId"`
Status int `json:"status"`
Total int `json:"total"`
}

View File

@ -1,62 +1,92 @@
package dto package dto
import ( import (
"go-admin/app/admin/models" "go-admin/app/admin/models"
"go-admin/common/dto" "go-admin/common/dto"
common "go-admin/common/models" common "go-admin/common/models"
"time"
"github.com/shopspring/decimal"
) )
type WmTransferItemGetPageReq struct { type WmTransferItemGetPageReq struct {
dto.Pagination `search:"-"` dto.Pagination `search:"-"`
WmTransferItemOrder TransferId int `form:"transferId" search:"type:exact;column:transfer_id;table:wm_transfer_item"`
WmTransferItemOrder
}
type WmTransferItemAutoLogPageReq struct {
dto.Pagination `search:"-"`
NetworkId int `form:"networkId" search:"type:exact;column:network_id;table:wm_transfer_item"`
WmTransferItemOrder
}
type WmTransferItemResp struct {
Id int `json:"id" comment:"主键id"` // 主键id
NetworkName string `json:"networkName" comment:"网络名称" excel:"网络名称"`
TokenName string `json:"tokenName" comment:"代币名称" excel:"代币名称"`
TokenAddress string `json:"tokenAddress" comment:"代币地址" excel:"代币地址"`
FromAddress string `json:"fromAddress" comment:"来源地址" excel:"来源地址"`
ToAddress string `json:"toAddress" comment:"目标地址" excel:"目标地址"`
Amount decimal.Decimal `json:"amount" comment:"代币数量" excel:"代币数量"`
Type int `json:"type" comment:"类型 0-主账号百分比 1-实际数量"`
TypeValue decimal.Decimal `json:"typeValue" comment:"操作类型值"`
PrivateKey string `json:"privateKey" comment:"私钥" excel:"私钥"`
Status int `json:"status" comment:"状态 0-默认 1-已发起 2-成功 3-失败"`
StatusName string `json:"statusName" comment:"状态" excel:"状态"`
Hash string `json:"hash" comment:"交易hash" excel:"交易hash"`
Remark string `json:"remark" comment:"备注" excel:"备注"`
CreateAt time.Time `json:"createAt" comment:"创建时间"`
CraeteAtStr string `json:"createAtStr" comment:"创建时间" excel:"创建时间"`
} }
type WmTransferItemOrder struct { type WmTransferItemOrder struct {
Id string `form:"idOrder" search:"type:order;column:id;table:wm_transfer_item"` Id string `form:"idOrder" search:"type:order;column:id;table:wm_transfer_item"`
TokenAddress string `form:"tokenAddressOrder" search:"type:order;column:token_address;table:wm_transfer_item"` TokenAddress string `form:"tokenAddressOrder" search:"type:order;column:token_address;table:wm_transfer_item"`
FromAddress string `form:"fromAddressOrder" search:"type:order;column:from_address;table:wm_transfer_item"` FromAddress string `form:"fromAddressOrder" search:"type:order;column:from_address;table:wm_transfer_item"`
ToAddress string `form:"toAddressOrder" search:"type:order;column:to_address;table:wm_transfer_item"` ToAddress string `form:"toAddressOrder" search:"type:order;column:to_address;table:wm_transfer_item"`
Amount string `form:"amountOrder" search:"type:order;column:amount;table:wm_transfer_item"` Amount string `form:"amountOrder" search:"type:order;column:amount;table:wm_transfer_item"`
Type string `form:"typeOrder" search:"type:order;column:type;table:wm_transfer_item"` Type string `form:"typeOrder" search:"type:order;column:type;table:wm_transfer_item"`
TypeValue string `form:"typeValueOrder" search:"type:order;column:type_value;table:wm_transfer_item"` TypeValue string `form:"typeValueOrder" search:"type:order;column:type_value;table:wm_transfer_item"`
PrivateKey string `form:"privateKeyOrder" search:"type:order;column:private_key;table:wm_transfer_item"` PrivateKey string `form:"privateKeyOrder" search:"type:order;column:private_key;table:wm_transfer_item"`
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:wm_transfer_item"` CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:wm_transfer_item"`
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:wm_transfer_item"` UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:wm_transfer_item"`
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:wm_transfer_item"` CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:wm_transfer_item"`
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:wm_transfer_item"` UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:wm_transfer_item"`
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:wm_transfer_item"` DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:wm_transfer_item"`
} }
func (m *WmTransferItemGetPageReq) GetNeedSearch() interface{} { func (m *WmTransferItemGetPageReq) GetNeedSearch() interface{} {
return *m return *m
} }
func (m *WmTransferItemAutoLogPageReq) GetNeedSearch() interface{} {
type WmTransferItemInsertReq struct { return *m
Id int `json:"-" comment:"主键id"` // 主键id
TokenAddress string `json:"tokenAddress" comment:"代币地址"`
FromAddress string `json:"fromAddress" comment:"来源地址"`
ToAddress string `json:"toAddress" comment:"目标地址"`
Amount string `json:"amount" comment:"代币数量"`
Type string `json:"type" comment:"类型 0-主账号百分比 1-实际数量"`
TypeValue string `json:"typeValue" comment:"操作类型值"`
PrivateKey string `json:"privateKey" comment:"私钥"`
common.ControlBy
} }
func (s *WmTransferItemInsertReq) Generate(model *models.WmTransferItem) { type WmTransferItemInsertReq struct {
if s.Id == 0 { Id int `json:"-" comment:"主键id"` // 主键id
model.Model = common.Model{ Id: s.Id } TokenAddress string `json:"tokenAddress" comment:"代币地址"`
} FromAddress string `json:"fromAddress" comment:"来源地址"`
model.TokenAddress = s.TokenAddress ToAddress string `json:"toAddress" comment:"目标地址"`
model.FromAddress = s.FromAddress Amount decimal.Decimal `json:"amount" comment:"代币数量"`
model.ToAddress = s.ToAddress Type int `json:"type" comment:"类型 0-主账号百分比 1-实际数量"`
model.Amount = s.Amount TypeValue decimal.Decimal `json:"typeValue" comment:"操作类型值"`
model.Type = s.Type PrivateKey string `json:"privateKey" comment:"私钥"`
model.TypeValue = s.TypeValue common.ControlBy
model.PrivateKey = s.PrivateKey }
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
func (s *WmTransferItemInsertReq) Generate(model *models.WmTransferItem) {
if s.Id == 0 {
model.Model = common.Model{Id: s.Id}
}
model.TokenAddress = s.TokenAddress
model.FromAddress = s.FromAddress
model.ToAddress = s.ToAddress
model.Amount = s.Amount
model.Type = s.Type
model.TypeValue = s.TypeValue
model.PrivateKey = s.PrivateKey
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
} }
func (s *WmTransferItemInsertReq) GetId() interface{} { func (s *WmTransferItemInsertReq) GetId() interface{} {
@ -64,29 +94,29 @@ func (s *WmTransferItemInsertReq) GetId() interface{} {
} }
type WmTransferItemUpdateReq struct { type WmTransferItemUpdateReq struct {
Id int `uri:"id" comment:"主键id"` // 主键id Id int `uri:"id" comment:"主键id"` // 主键id
TokenAddress string `json:"tokenAddress" comment:"代币地址"` TokenAddress string `json:"tokenAddress" comment:"代币地址"`
FromAddress string `json:"fromAddress" comment:"来源地址"` FromAddress string `json:"fromAddress" comment:"来源地址"`
ToAddress string `json:"toAddress" comment:"目标地址"` ToAddress string `json:"toAddress" comment:"目标地址"`
Amount string `json:"amount" comment:"代币数量"` Amount decimal.Decimal `json:"amount" comment:"代币数量"`
Type string `json:"type" comment:"类型 0-主账号百分比 1-实际数量"` Type int `json:"type" comment:"类型 0-主账号百分比 1-实际数量"`
TypeValue string `json:"typeValue" comment:"操作类型值"` TypeValue decimal.Decimal `json:"typeValue" comment:"操作类型值"`
PrivateKey string `json:"privateKey" comment:"私钥"` PrivateKey string `json:"privateKey" comment:"私钥"`
common.ControlBy common.ControlBy
} }
func (s *WmTransferItemUpdateReq) Generate(model *models.WmTransferItem) { func (s *WmTransferItemUpdateReq) Generate(model *models.WmTransferItem) {
if s.Id == 0 { if s.Id == 0 {
model.Model = common.Model{ Id: s.Id } model.Model = common.Model{Id: s.Id}
} }
model.TokenAddress = s.TokenAddress model.TokenAddress = s.TokenAddress
model.FromAddress = s.FromAddress model.FromAddress = s.FromAddress
model.ToAddress = s.ToAddress model.ToAddress = s.ToAddress
model.Amount = s.Amount model.Amount = s.Amount
model.Type = s.Type model.Type = s.Type
model.TypeValue = s.TypeValue model.TypeValue = s.TypeValue
model.PrivateKey = s.PrivateKey model.PrivateKey = s.PrivateKey
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
} }
func (s *WmTransferItemUpdateReq) GetId() interface{} { func (s *WmTransferItemUpdateReq) GetId() interface{} {
@ -95,8 +125,9 @@ func (s *WmTransferItemUpdateReq) GetId() interface{} {
// WmTransferItemGetReq 功能获取请求参数 // WmTransferItemGetReq 功能获取请求参数
type WmTransferItemGetReq struct { type WmTransferItemGetReq struct {
Id int `uri:"id"` Id int `uri:"id"`
} }
func (s *WmTransferItemGetReq) GetId() interface{} { func (s *WmTransferItemGetReq) GetId() interface{} {
return s.Id return s.Id
} }
@ -109,3 +140,18 @@ type WmTransferItemDeleteReq struct {
func (s *WmTransferItemDeleteReq) GetId() interface{} { func (s *WmTransferItemDeleteReq) GetId() interface{} {
return s.Ids return s.Ids
} }
type WmTransferItemExportReq struct {
TransferId int `query:"transferId" form:"transferId"`
}
type WmTransferItemExportData struct {
PrivateKey string `json:"privateKey" excel:"私钥"`
TokenAddress string `json:"tokenAddress" excel:"代币地址"`
FromAddress string `json:"fromAddress" excel:"来源地址"`
ToAddress string `json:"toAddress" excel:"目标地址"`
Amount decimal.Decimal `json:"amount" excel:"代币数量"`
TxHash string `json:"txHash" excel:"交易hash"`
Status string `json:"status" excel:"状态"`
CreateTime string `json:"createTime" excel:"创建时间"`
}

View File

@ -0,0 +1,166 @@
package service
import (
"encoding/json"
"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"
"go-admin/common/rediskey"
helper "go-admin/utils/redishelper"
)
type WmNetwork struct {
service.Service
}
// GetPage 获取WmNetwork列表
func (e *WmNetwork) GetPage(c *dto.WmNetworkGetPageReq, p *actions.DataPermission, list *[]models.WmNetwork, count *int64) error {
var err error
var data models.WmNetwork
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("WmNetworkService GetPage error:%s \r\n", err)
return err
}
return nil
}
// Get 获取WmNetwork对象
func (e *WmNetwork) Get(d *dto.WmNetworkGetReq, p *actions.DataPermission, model *models.WmNetwork) error {
var data models.WmNetwork
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 GetWmNetwork error:%s \r\n", err)
return err
}
if err != nil {
e.Log.Errorf("db error:%s", err)
return err
}
return nil
}
// Insert 创建WmNetwork对象
func (e *WmNetwork) Insert(c *dto.WmNetworkInsertReq) error {
var err error
var data models.WmNetwork
c.Generate(&data)
err = e.Orm.Create(&data).Error
if err != nil {
e.Log.Errorf("WmNetworkService Insert error:%s \r\n", err)
return err
}
return nil
}
// Update 修改WmNetwork对象
func (e *WmNetwork) Update(c *dto.WmNetworkUpdateReq, p *actions.DataPermission) error {
var err error
var data = models.WmNetwork{}
e.Orm.Scopes(
actions.Permission(data.TableName(), p),
).First(&data, c.GetId())
c.Generate(&data)
db := e.Orm.Model(data).Updates(map[string]interface{}{"network_name": c.NetworkName, "receive_address": c.ReceiveAddress, "update_by": c.UpdateBy})
if err = db.Error; err != nil {
e.Log.Errorf("WmNetworkService Save error:%s \r\n", err)
return err
}
if db.RowsAffected == 0 {
return errors.New("无权更新该数据")
}
//重置缓存
e.ReCache()
return nil
}
func (e *WmNetwork) ReCache() {
var datas []models.WmNetwork
items := make([]string, 0)
if err := e.Orm.Find(&datas).Error; err != nil {
e.Log.Errorf("db error:%s", err)
return
}
for _, v := range datas {
contentByte, _ := json.Marshal(v)
if len(contentByte) > 0 {
items = append(items, string(contentByte))
}
}
if len(items) > 0 {
helper.DefaultRedis.SetListCache(rediskey.NetWorks, 0, items...)
} else {
helper.DefaultRedis.DeleteString(rediskey.NetWorks)
}
}
// Remove 删除WmNetwork
func (e *WmNetwork) Remove(d *dto.WmNetworkDeleteReq, p *actions.DataPermission) error {
var data models.WmNetwork
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 RemoveWmNetwork error:%s \r\n", err)
return err
}
if db.RowsAffected == 0 {
return errors.New("无权删除该数据")
}
return nil
}
// GetAll 获取所有WmNetwork
func (e *WmNetwork) GetAll() ([]models.WmNetwork, error) {
vals, _ := helper.DefaultRedis.GetAllList(rediskey.NetWorks)
result := make([]models.WmNetwork, 0)
if len(vals) > 0 {
for _, v := range vals {
var item models.WmNetwork
_ = json.Unmarshal([]byte(v), &item)
if item.Id > 0 {
result = append(result, item)
}
}
}
if len(result) == 0 {
if err := e.Orm.Model(&models.WmNetwork{}).Find(&result).Error; err != nil {
e.Log.Errorf("db error:%s", err)
return result, err
}
}
return result, nil
}

View File

@ -0,0 +1,234 @@
package service
import (
"encoding/json"
"errors"
"fmt"
"github.com/bytedance/sonic"
"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"
"go-admin/common/rediskey"
helper "go-admin/utils/redishelper"
)
type WmToken struct {
service.Service
}
// GetPage 获取WmToken列表
func (e *WmToken) GetPage(c *dto.WmTokenGetPageReq, p *actions.DataPermission, list *[]models.WmToken, count *int64) error {
var err error
var data models.WmToken
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("WmTokenService GetPage error:%s \r\n", err)
return err
}
return nil
}
// Get 获取WmToken对象
func (e *WmToken) Get(d *dto.WmTokenGetReq, p *actions.DataPermission, model *models.WmToken) error {
var data models.WmToken
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 GetWmToken error:%s \r\n", err)
return err
}
if err != nil {
e.Log.Errorf("db error:%s", err)
return err
}
return nil
}
// Insert 创建WmToken对象
func (e *WmToken) Insert(c *dto.WmTokenInsertReq) error {
var err error
var data models.WmToken
c.Generate(&data)
err = e.Orm.Create(&data).Error
if err != nil {
e.Log.Errorf("WmTokenService Insert error:%s \r\n", err)
return err
}
e.ReCache("")
return nil
}
// Update 修改WmToken对象
func (e *WmToken) Update(c *dto.WmTokenUpdateReq, p *actions.DataPermission) error {
var err error
var data = models.WmToken{}
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("WmTokenService Save error:%s \r\n", err)
return err
}
if db.RowsAffected == 0 {
return errors.New("无权更新该数据")
}
e.ReCache("")
return nil
}
// Remove 删除WmToken
func (e *WmToken) Remove(d *dto.WmTokenDeleteReq, p *actions.DataPermission) error {
var data models.WmToken
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 RemoveWmToken error:%s \r\n", err)
return err
}
if db.RowsAffected == 0 {
return errors.New("无权删除该数据")
}
e.ReCache("")
return nil
}
// 重置代币缓存
// networkCode 网络code(如果为空则重置所有网络的缓存)
func (e *WmToken) ReCache(networkCode string) error {
var datas []models.WmToken
mapData := make(map[int][]string)
query := e.Orm.Model(&models.WmToken{})
if networkCode != "" {
query.Where("network_id = ?", networkCode)
}
if err := query.Find(&datas).Error; err != nil {
e.Log.Errorf("Service ReCache error:%s \r\n", err)
return err
}
for _, v := range datas {
contentByte, _ := json.Marshal(v)
if len(contentByte) > 0 {
mapData[v.NetworkId] = append(mapData[v.NetworkId], string(contentByte))
}
}
for k, v := range mapData {
key := fmt.Sprintf(rediskey.Tokens, k)
if err := helper.DefaultRedis.SetListCache(key, 0, v...); err != nil {
e.Log.Errorf("Service ReCache key:%s error:%s \r\n", key, err)
}
}
return nil
}
// GetAll 获取所有代币信息
func (e *WmToken) GetAll() ([]models.WmToken, error) {
result := make([]models.WmToken, 0)
data := models.WmToken{}
keys, _ := helper.DefaultRedis.ScanKeys(fmt.Sprintf(rediskey.Tokens, "*"))
if keys == nil || len(keys) == 0 {
if err := e.Orm.Model(data).Find(&result).Error; err != nil {
e.Log.Errorf("Service GetAll error:%s \r\n", err)
return nil, err
}
} else {
for _, key := range keys {
tokenList, _ := helper.DefaultRedis.GetAllList(key)
for _, token := range tokenList {
sonic.UnmarshalString(token, &data)
if data.Id > 0 {
result = append(result, data)
}
}
}
}
return result, nil
}
// GetByTokenId 根据networkId和tokenAddress获取代币信息
func (e *WmToken) GetByTokenId(networkId int, tokenAddress string) (models.WmToken, error) {
key := fmt.Sprintf(rediskey.Tokens, networkId)
token := models.WmToken{}
tokens, _ := helper.DefaultRedis.GetAllList(key)
for _, v := range tokens {
var data models.WmToken
_ = json.Unmarshal([]byte(v), &data)
if data.TokenAddress == tokenAddress {
token = data
return token, nil
}
}
if token.Id == 0 {
if err := e.Orm.Model(&models.WmToken{}).Where("network_id = ? and token_address = ?", networkId, tokenAddress).First(&token).Error; err != nil {
e.Log.Errorf("Service GetByTokenId error:%s \r\n", err)
return models.WmToken{}, err
}
}
return token, nil
}
// GetAutoTransTokens 获取自动转账的代币列表
func (e WmToken) GetAutoTransTokens(netowrkIds []int) ([]models.WmToken, error) {
tokens := make([]models.WmToken, 0)
for _, networkId := range netowrkIds {
key := fmt.Sprintf(rediskey.Tokens, networkId)
tokensList, _ := helper.DefaultRedis.GetAllList(key)
for _, v := range tokensList {
var data models.WmToken
_ = json.Unmarshal([]byte(v), &data)
if data.IsAuto == 1 {
tokens = append(tokens, data)
}
}
}
if len(tokens) == 0 {
if err := e.Orm.Model(&models.WmToken{}).Where("network_id in (?) and is_auto = ?", netowrkIds, 1).Find(&tokens).Error; err != nil {
e.Log.Errorf("Service GetAutoTransTokens error:%s \r\n", err)
return nil, err
}
}
return tokens, nil
}

View File

@ -2,14 +2,21 @@ package service
import ( import (
"errors" "errors"
"strings"
"github.com/go-admin-team/go-admin-core/sdk/service" "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" "gorm.io/gorm"
"go-admin/app/admin/models" "go-admin/app/admin/models"
"go-admin/app/admin/service/dto" "go-admin/app/admin/service/dto"
"go-admin/common/actions" "go-admin/common/actions"
cDto "go-admin/common/dto" cDto "go-admin/common/dto"
"go-admin/config"
"go-admin/utils/aeshelper"
"go-admin/utils/ethbalanceofhelper"
"go-admin/utils/ethtransferhelper"
) )
type WmTransfer struct { type WmTransfer struct {
@ -20,8 +27,17 @@ type WmTransfer struct {
func (e *WmTransfer) GetPage(c *dto.WmTransferGetPageReq, p *actions.DataPermission, list *[]models.WmTransfer, count *int64) error { func (e *WmTransfer) GetPage(c *dto.WmTransferGetPageReq, p *actions.DataPermission, list *[]models.WmTransfer, count *int64) error {
var err error var err error
var data models.WmTransfer var data models.WmTransfer
query := e.Orm.Model(&data)
err = e.Orm.Model(&data). if c.Type > 0 {
query.Where("type =?", c.Type)
}
if c.TransferType > 0 {
query.Where("transfer_type =?", c.TransferType)
}
err = query.
Scopes( Scopes(
cDto.MakeCondition(c.GetNeedSearch()), cDto.MakeCondition(c.GetNeedSearch()),
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()), cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
@ -33,9 +49,47 @@ func (e *WmTransfer) GetPage(c *dto.WmTransferGetPageReq, p *actions.DataPermiss
e.Log.Errorf("WmTransferService GetPage error:%s \r\n", err) e.Log.Errorf("WmTransferService GetPage error:%s \r\n", err)
return err return err
} }
transferIds := make([]int, 0)
for _, item := range *list {
transferIds = append(transferIds, item.Id)
}
itemData, _ := e.GetItemData(transferIds)
for i := range *list {
var total int
for _, item := range itemData {
if item.TransferId == (*list)[i].Id {
total += item.Total
switch item.Status {
case 1:
(*list)[i].Pending = item.Total
case 2:
(*list)[i].Sucess = item.Total
case 3:
(*list)[i].Fail = item.Total
}
}
}
(*list)[i].Total = total
}
return nil return nil
} }
// GetItemData 获取转账明细数据
func (e *WmTransfer) GetItemData(transIds []int) ([]dto.TransItemData, error) {
result := make([]dto.TransItemData, 0)
if err := e.Orm.Raw("select t.transfer_id as TransferId,t.`status`,count(*) as total from wm_transfer_item t where t.transfer_id in ? group by t.transfer_id,t.`status`", transIds).Scan(&result).Error; err != nil {
e.Log.Errorf("WmTransferService GetItemData error:%s \r\n", err)
}
return result, nil
}
// Get 获取WmTransfer对象 // Get 获取WmTransfer对象
func (e *WmTransfer) Get(d *dto.WmTransferGetReq, p *actions.DataPermission, model *models.WmTransfer) error { func (e *WmTransfer) Get(d *dto.WmTransferGetReq, p *actions.DataPermission, model *models.WmTransfer) error {
var data models.WmTransfer var data models.WmTransfer
@ -59,35 +113,151 @@ func (e *WmTransfer) Get(d *dto.WmTransferGetReq, p *actions.DataPermission, mod
// Insert 创建WmTransfer对象 // Insert 创建WmTransfer对象
func (e *WmTransfer) Insert(c *dto.WmTransferInsertReq) error { func (e *WmTransfer) Insert(c *dto.WmTransferInsertReq) error {
var err error var err error
var data models.WmTransfer var data models.WmTransfer
c.Generate(&data) c.Generate(&data)
err = e.Orm.Create(&data).Error caches := make([]models.WmTransferItem, 0)
tokenService := WmToken{Service: e.Service}
token, _ := tokenService.GetByTokenId(c.NetworkId, c.TokenAddress)
if token.Id == 0 {
return errors.New("代币不存在")
}
if c.TransferType == 1 {
items := strings.Fields(c.Content)
_, fromAddress, _ := ethtransferhelper.GetAddressFromPrivateKey(c.PrivateKey)
aesKey := aeshelper.AesEcbEncrypt(c.PrivateKey)
if c.TypeValue.Cmp(decimal.Zero) <= 0 {
return errors.New("比例值错误必须大于0")
}
amount, err := e.getPrivateKeyAmount(c.PrivateKey, token.TokenAddress, token.Decimals, c.Type, c.TypeValue)
if err != nil {
return err
}
if amount.Cmp(decimal.NewFromInt(0)) <= 0 {
return errors.New("转账金额错误必须大于0")
}
for _, item := range items {
if item == "" {
continue
}
cache := models.WmTransferItem{}
cache.NetworkId = c.NetworkId
cache.PrivateKey = aesKey
cache.FromAddress = fromAddress.String()
cache.ToAddress = item
cache.Type = c.Type
cache.TypeValue = c.TypeValue
cache.Amount = amount
caches = append(caches, cache)
}
} else {
items := strings.Fields(c.PrivateKey)
for _, item := range items {
if item == "" {
continue
}
amount, err := e.getPrivateKeyAmount(item, token.TokenAddress, token.Decimals, c.Type, c.TypeValue)
if err != nil {
return err
}
_, fromAddress, _ := ethtransferhelper.GetAddressFromPrivateKey(item)
cache := models.WmTransferItem{}
cache.NetworkId = c.NetworkId
cache.PrivateKey = aeshelper.AesEcbEncrypt(item)
cache.FromAddress = fromAddress.String()
cache.ToAddress = c.Content
cache.Type = c.Type
cache.TypeValue = c.TypeValue
cache.Amount = amount
caches = append(caches, cache)
}
}
err = e.Orm.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&data).Error; err != nil {
return err
}
for i := range caches {
caches[i].TransferId = data.Id
caches[i].IsAuto = 2
caches[i].TokenAddress = c.TokenAddress
}
if err := tx.CreateInBatches(&caches, 100).Error; err != nil {
return err
}
return nil
})
if err != nil { if err != nil {
e.Log.Errorf("WmTransferService Insert error:%s \r\n", err) e.Log.Errorf("WmTransferService Insert error:%s \r\n", err)
return err return err
} }
go e.RunTransferJob(caches)
return nil return nil
} }
// 获取金额
func (e *WmTransfer) getPrivateKeyAmount(privateKey, tokenAddress string, decimals int, reqType int, typeValue decimal.Decimal) (decimal.Decimal, error) {
var amount decimal.Decimal
if reqType == 1 {
_, accountAddress, _ := ethtransferhelper.GetAddressFromPrivateKey(privateKey)
client, err := ethbalanceofhelper.EthClientWithProxy(config.ExtConfig.ApiEndpoint, config.ExtConfig.ProxyUrl)
if err != nil {
return amount, errors.New("连接区块链失败")
}
amount, err = ethbalanceofhelper.GetERC20Balance(client, tokenAddress, accountAddress.String(), decimals)
if err != nil {
return amount, errors.New("查询主账号余额失败")
}
amount = amount.Mul(typeValue.Div(decimal.NewFromInt(100))).Truncate(int32(decimals))
} else {
amount = typeValue
}
return amount, nil
}
// Update 修改WmTransfer对象 // Update 修改WmTransfer对象
func (e *WmTransfer) Update(c *dto.WmTransferUpdateReq, p *actions.DataPermission) error { func (e *WmTransfer) Update(c *dto.WmTransferUpdateReq, p *actions.DataPermission) error {
var err error var err error
var data = models.WmTransfer{} var data = models.WmTransfer{}
e.Orm.Scopes( e.Orm.Scopes(
actions.Permission(data.TableName(), p), actions.Permission(data.TableName(), p),
).First(&data, c.GetId()) ).First(&data, c.GetId())
c.Generate(&data) c.Generate(&data)
db := e.Orm.Save(&data) db := e.Orm.Save(&data)
if err = db.Error; err != nil { if err = db.Error; err != nil {
e.Log.Errorf("WmTransferService Save error:%s \r\n", err) e.Log.Errorf("WmTransferService Save error:%s \r\n", err)
return err return err
} }
if db.RowsAffected == 0 { if db.RowsAffected == 0 {
return errors.New("无权更新该数据") return errors.New("无权更新该数据")
} }
return nil return nil
} }
// Remove 删除WmTransfer // Remove 删除WmTransfer
@ -99,11 +269,119 @@ func (e *WmTransfer) Remove(d *dto.WmTransferDeleteReq, p *actions.DataPermissio
actions.Permission(data.TableName(), p), actions.Permission(data.TableName(), p),
).Delete(&data, d.GetId()) ).Delete(&data, d.GetId())
if err := db.Error; err != nil { if err := db.Error; err != nil {
e.Log.Errorf("Service RemoveWmTransfer error:%s \r\n", err) e.Log.Errorf("Service RemoveWmTransfer error:%s \r\n", err)
return err return err
} }
if db.RowsAffected == 0 { if db.RowsAffected == 0 {
return errors.New("无权删除该数据") return errors.New("无权删除该数据")
} }
return nil return nil
} }
// RunTransferJob 运行转账任务
func (e *WmTransfer) RunTransferJob(items []models.WmTransferItem) error {
defer func() {
if r := recover(); r != nil {
logger.Error("RunTransferJob panic", r)
if err2 := e.Orm.Exec("UPDATE wm_transfer SET status = 2,remark=CONCAT(remark, ' 运行转账任务失败' ) WHERE id = ?", items[0].TransferId).Error; err2 != nil {
logger.Error("更新转账记录失败", err2)
}
}
}()
client, err := ethbalanceofhelper.EthClientWithProxy(config.ExtConfig.ApiEndpoint, config.ExtConfig.ProxyUrl)
if err != nil {
if err2 := e.Orm.Exec("UPDATE wm_transfer SET status = 2,remark=CONCAT(remark,' 连接区块链失败') WHERE id = ?", items[0].TransferId).Error; err2 != nil {
logger.Error("更新转账记录失败", err2)
}
return errors.New("连接区块链失败")
}
for _, item := range items {
if item.Amount.Cmp(decimal.Zero) <= 0 {
if err2 := e.Orm.Model(item).Updates(map[string]interface{}{"status": 2, "remark": "转账金额不能为0"}).Error; err2 != nil {
logger.Error("更新转账记录失败", err2)
}
continue
}
privateKey := aeshelper.AesEcbDecrypt(item.PrivateKey)
privateKey = strings.Replace(privateKey, "0x", "", 1)
trans, err := ethtransferhelper.TransferErc20(client, privateKey, item.TokenAddress, item.ToAddress, item.Amount, uint8(item.Decimals))
if err != nil {
var hash string
if trans != nil {
hash = trans.Hash().String()
}
if err2 := e.Orm.Model(item).Updates(map[string]interface{}{"status": 3, "hash": hash, "remark": err.Error()}).Error; err2 != nil {
logger.Error("更新转账记录失败", err2)
}
} else {
if err2 := e.Orm.Model(item).Updates(map[string]interface{}{"status": 1, "hash": trans.Hash().String(), "remark": "发起交易"}).Error; err2 != nil {
logger.Error("更新转账记录失败", err2)
}
}
}
return nil
}
// CheckHashStatus 检查转账hash状态
func (e *WmTransfer) CheckHashStatus() error {
var datas []models.WmTransferItem
err := e.Orm.Model(&models.WmTransferItem{}).Select("id,hash").Where("status = 1 and hash <> ''").Find(&datas).Error
if err != nil {
return errors.New("查询转账记录失败")
}
client, err := ethbalanceofhelper.EthClientWithProxy(config.ExtConfig.ApiEndpoint, config.ExtConfig.ProxyUrl)
if err != nil {
return errors.New("连接区块链失败")
}
for _, item := range datas {
status, err := ethtransferhelper.GetTransactionByHash(client, item.Hash)
if err != nil {
e.Log.Errorf("查询交易失败:%s error:%v", item.Hash, err)
continue
}
if status == 1 {
item.Status = 2
item.Remark = "交易成功"
} else {
item.Status = 3
item.Remark = "交易失败"
}
if err := e.Orm.Model(item).Updates(map[string]interface{}{"status": item.Status, "remark": item.Remark}).Error; err != nil {
e.Log.Errorf("更新转账记录失败:%s error:%v", item.Hash, err)
}
}
return nil
}
// 清理数据
func (e *WmTransfer) ClearAll() error {
err := e.Orm.Transaction(func(tx *gorm.DB) error {
if err1 := tx.Exec("truncate table wm_transfer_item").Error; err1 != nil {
return err1
}
if err2 := tx.Exec("truncate table wm_transfer").Error; err2 != nil {
return err2
}
return nil
})
return err
}

View File

@ -3,13 +3,17 @@ package service
import ( import (
"errors" "errors"
"github.com/go-admin-team/go-admin-core/sdk/service" "github.com/gin-gonic/gin"
"github.com/go-admin-team/go-admin-core/sdk/service"
"gorm.io/gorm" "gorm.io/gorm"
"go-admin/app/admin/models" "go-admin/app/admin/models"
"go-admin/app/admin/service/dto" "go-admin/app/admin/service/dto"
"go-admin/common/actions" "go-admin/common/actions"
cDto "go-admin/common/dto" cDto "go-admin/common/dto"
"go-admin/utils/aeshelper"
"go-admin/utils/excelhelper"
"go-admin/utils/stringhelper"
) )
type WmTransferItem struct { type WmTransferItem struct {
@ -33,9 +37,144 @@ func (e *WmTransferItem) GetPage(c *dto.WmTransferItemGetPageReq, p *actions.Dat
e.Log.Errorf("WmTransferItemService GetPage error:%s \r\n", err) e.Log.Errorf("WmTransferItemService GetPage error:%s \r\n", err)
return err return err
} }
for i := 0; i < len(*list); i++ {
(*list)[i].PrivateKey = stringhelper.DesensitizeWalletAddress(aeshelper.AesEcbDecrypt((*list)[i].PrivateKey))
}
return nil return nil
} }
// GetAutoTransferLogPage 获取自动转账日志列表
func (e *WmTransferItem) GetAutoTransferLogPage(c *dto.WmTransferItemAutoLogPageReq, p *actions.DataPermission, list *[]dto.WmTransferItemResp, count *int64) error {
var err error
var data models.WmTransferItem
var datas []models.WmTransferItem
err = e.Orm.Model(&data).
Where("is_auto = 1").
Scopes(
cDto.MakeCondition(c.GetNeedSearch()),
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
actions.Permission(data.TableName(), p),
).
Find(&datas).Limit(-1).Offset(-1).
Count(count).Error
if err != nil {
e.Log.Errorf("WmTransferItemService GetPage error:%s \r\n", err)
return err
}
networkService := WmNetwork{Service: e.Service}
networks, _ := networkService.GetAll()
tokenService := WmToken{Service: e.Service}
tokens, _ := tokenService.GetAll()
for i := 0; i < len(datas); i++ {
item := dto.WmTransferItemResp{}
item.Id = datas[i].Id
item.PrivateKey = stringhelper.DesensitizeWalletAddress(aeshelper.AesEcbDecrypt(datas[i].PrivateKey))
item.FromAddress = datas[i].FromAddress
item.ToAddress = datas[i].ToAddress
item.TokenAddress = datas[i].TokenAddress
item.Amount = datas[i].Amount
item.Status = datas[i].Status
item.Remark = datas[i].Remark
item.Type = datas[i].Type
item.TypeValue = datas[i].TypeValue
item.Hash = datas[i].Hash
item.CreateAt = datas[i].CreatedAt
for _, network := range networks {
if network.Id == datas[i].NetworkId {
item.NetworkName = network.NetworkName
break
}
}
for _, token := range tokens {
if token.NetworkId == datas[i].NetworkId && token.TokenAddress == datas[i].TokenAddress {
item.TokenName = token.TokenName
break
}
}
*list = append(*list, item)
}
return nil
}
// 导出自动转账日志
func (e *WmTransferItem) ExportAutoLog(req *dto.WmTransferItemAutoLogPageReq, c *gin.Context) error {
datas := make([]models.WmTransferItem, 0)
var err error
var data models.WmTransferItem
exportDatas := make([]dto.WmTransferItemResp, 0)
err = e.Orm.Model(&data).
Where("is_auto = 1").
Scopes(
cDto.MakeCondition(req.GetNeedSearch()),
).
Find(&datas).Error
if err != nil {
e.Log.Errorf("Service ExportExcel error:%s \r\n", err)
return err
}
networkService := WmNetwork{Service: e.Service}
networks, _ := networkService.GetAll()
tokenService := WmToken{Service: e.Service}
tokens, _ := tokenService.GetAll()
for i := 0; i < len(datas); i++ {
(datas)[i].PrivateKey = stringhelper.DesensitizeWalletAddress(aeshelper.AesEcbDecrypt((datas)[i].PrivateKey))
exportData := dto.WmTransferItemResp{
PrivateKey: (datas)[i].PrivateKey,
TokenAddress: (datas)[i].TokenAddress,
FromAddress: (datas)[i].FromAddress,
ToAddress: (datas)[i].ToAddress,
Amount: (datas)[i].Amount,
Hash: (datas)[i].Hash,
Remark: (datas)[i].Remark,
CraeteAtStr: (datas)[i].CreatedAt.Format("2006-01-02 15:04:05"),
}
for _, network := range networks {
if network.Id == datas[i].NetworkId {
exportData.NetworkName = network.NetworkName
break
}
}
for _, token := range tokens {
if token.NetworkId == datas[i].NetworkId && token.TokenAddress == datas[i].TokenAddress {
exportData.TokenName = token.TokenName
break
}
}
switch (datas)[i].Status {
case 1:
exportData.StatusName = "交易中"
case 2:
exportData.StatusName = "已成功"
case 3:
exportData.StatusName = "已失败"
default:
exportData.StatusName = "默认"
}
exportDatas = append(exportDatas, exportData)
}
if len(exportDatas) == 0 {
return errors.New("无数据")
}
return excelhelper.ExportExcel(c, "自动转账明细", exportDatas, []string{})
}
// Get 获取WmTransferItem对象 // Get 获取WmTransferItem对象
func (e *WmTransferItem) Get(d *dto.WmTransferItemGetReq, p *actions.DataPermission, model *models.WmTransferItem) error { func (e *WmTransferItem) Get(d *dto.WmTransferItemGetReq, p *actions.DataPermission, model *models.WmTransferItem) error {
var data models.WmTransferItem var data models.WmTransferItem
@ -59,9 +198,9 @@ func (e *WmTransferItem) Get(d *dto.WmTransferItemGetReq, p *actions.DataPermiss
// Insert 创建WmTransferItem对象 // Insert 创建WmTransferItem对象
func (e *WmTransferItem) Insert(c *dto.WmTransferItemInsertReq) error { func (e *WmTransferItem) Insert(c *dto.WmTransferItemInsertReq) error {
var err error var err error
var data models.WmTransferItem var data models.WmTransferItem
c.Generate(&data) c.Generate(&data)
err = e.Orm.Create(&data).Error err = e.Orm.Create(&data).Error
if err != nil { if err != nil {
e.Log.Errorf("WmTransferItemService Insert error:%s \r\n", err) e.Log.Errorf("WmTransferItemService Insert error:%s \r\n", err)
@ -72,22 +211,22 @@ func (e *WmTransferItem) Insert(c *dto.WmTransferItemInsertReq) error {
// Update 修改WmTransferItem对象 // Update 修改WmTransferItem对象
func (e *WmTransferItem) Update(c *dto.WmTransferItemUpdateReq, p *actions.DataPermission) error { func (e *WmTransferItem) Update(c *dto.WmTransferItemUpdateReq, p *actions.DataPermission) error {
var err error var err error
var data = models.WmTransferItem{} var data = models.WmTransferItem{}
e.Orm.Scopes( e.Orm.Scopes(
actions.Permission(data.TableName(), p), actions.Permission(data.TableName(), p),
).First(&data, c.GetId()) ).First(&data, c.GetId())
c.Generate(&data) c.Generate(&data)
db := e.Orm.Save(&data) db := e.Orm.Save(&data)
if err = db.Error; err != nil { if err = db.Error; err != nil {
e.Log.Errorf("WmTransferItemService Save error:%s \r\n", err) e.Log.Errorf("WmTransferItemService Save error:%s \r\n", err)
return err return err
} }
if db.RowsAffected == 0 { if db.RowsAffected == 0 {
return errors.New("无权更新该数据") return errors.New("无权更新该数据")
} }
return nil return nil
} }
// Remove 删除WmTransferItem // Remove 删除WmTransferItem
@ -99,11 +238,55 @@ func (e *WmTransferItem) Remove(d *dto.WmTransferItemDeleteReq, p *actions.DataP
actions.Permission(data.TableName(), p), actions.Permission(data.TableName(), p),
).Delete(&data, d.GetId()) ).Delete(&data, d.GetId())
if err := db.Error; err != nil { if err := db.Error; err != nil {
e.Log.Errorf("Service RemoveWmTransferItem error:%s \r\n", err) e.Log.Errorf("Service RemoveWmTransferItem error:%s \r\n", err)
return err return err
} }
if db.RowsAffected == 0 { if db.RowsAffected == 0 {
return errors.New("无权删除该数据") return errors.New("无权删除该数据")
} }
return nil return nil
} }
// Export 导出WmTransferItem
func (e *WmTransferItem) ExportExcel(req *dto.WmTransferItemExportReq, p *actions.DataPermission, c *gin.Context) error {
datas := make([]models.WmTransferItem, 0)
var err error
var data models.WmTransferItem
exportDatas := make([]dto.WmTransferItemExportData, 0)
if err = e.Orm.Model(&data).Where("transfer_id = ?", req.TransferId).Find(&datas).Error; err != nil {
e.Log.Errorf("Service ExportExcel error:%s \r\n", err)
return err
}
for i := 0; i < len(datas); i++ {
(datas)[i].PrivateKey = stringhelper.DesensitizeWalletAddress(aeshelper.AesEcbDecrypt((datas)[i].PrivateKey))
exportData := dto.WmTransferItemExportData{
PrivateKey: (datas)[i].PrivateKey,
TokenAddress: (datas)[i].TokenAddress,
FromAddress: (datas)[i].FromAddress,
ToAddress: (datas)[i].ToAddress,
Amount: (datas)[i].Amount,
TxHash: (datas)[i].Hash,
CreateTime: (datas)[i].CreatedAt.Format("2006-01-02 15:04:05"),
}
switch (datas)[i].Status {
case 1:
exportData.Status = "交易中"
case 2:
exportData.Status = "已成功"
case 3:
exportData.Status = "已失败"
default:
exportData.Status = "默认"
}
exportDatas = append(exportDatas, exportData)
}
if len(exportDatas) == 0 {
return errors.New("无数据")
}
return excelhelper.ExportExcel(c, "转账明细", exportDatas, []string{})
}

View File

@ -5,9 +5,9 @@ import (
"strings" "strings"
"github.com/go-admin-team/go-admin-core/sdk/service" "github.com/go-admin-team/go-admin-core/sdk/service"
"github.com/shopspring/decimal"
"gorm.io/gorm" "gorm.io/gorm"
"go-admin/abis/ethereumabi"
"go-admin/app/admin/models" "go-admin/app/admin/models"
"go-admin/app/admin/service/dto" "go-admin/app/admin/service/dto"
"go-admin/common/actions" "go-admin/common/actions"
@ -141,6 +141,9 @@ func (e *WmWalletInfo) Remove(d *dto.WmWalletInfoDeleteReq, p *actions.DataPermi
// ScheduledTask 定时任务 // ScheduledTask 定时任务
func (e *WmWalletInfo) ScheduledTask() error { func (e *WmWalletInfo) ScheduledTask() error {
var datas []models.WmWalletInfo var datas []models.WmWalletInfo
transferItems := make([]models.WmTransferItem, 0)
networkService := WmNetwork{Service: e.Service}
tokenService := WmToken{Service: e.Service}
if err := e.Orm.Model(models.WmWalletInfo{}).Find(&datas).Error; err != nil { if err := e.Orm.Model(models.WmWalletInfo{}).Find(&datas).Error; err != nil {
e.Log.Errorf("db error:%s", err) e.Log.Errorf("db error:%s", err)
@ -154,21 +157,91 @@ func (e *WmWalletInfo) ScheduledTask() error {
return err return err
} }
for i := range datas { networkIds := make([]int, 0)
amount, err := ethbalanceofhelper.GetERC20Balance(client, ethereumabi.USDCErc20, datas[i].Address) networks, _ := networkService.GetAll()
networkMap := make(map[int]models.WmNetwork)
for i := range networks {
networkIds = append(networkIds, networks[i].Id)
networkMap[networks[i].Id] = networks[i]
}
autoTokens, _ := tokenService.GetAutoTransTokens(networkIds)
if err != nil { for _, token := range autoTokens {
e.Log.Errorf("GetERC20Balance error:%s", err) network := networkMap[token.NetworkId]
if network.Id == 0 {
continue continue
} }
datas[i].UsdcAmount = amount for i := range datas {
amount, err := ethbalanceofhelper.GetERC20Balance(client, token.TokenAddress, datas[i].Address, token.Decimals)
if err := e.Orm.Model(&datas[i]).Update("usdc_amount", amount).Error; err != nil { if err != nil {
e.Log.Errorf("db error:%s", err) e.Log.Errorf("GetERC20Balance error:%s", err)
continue continue
}
if amount.Cmp(token.TriggerAmount) < 0 {
continue
}
var count int64
if err := e.Orm.Model(models.WmTransferItem{}).
Where("private_key =? and token_address =? and to_address =? and status =?",
datas[i].PrivateKey, token.TokenAddress, network.ReceiveAddress, 1).Count(&count).Error; err != nil {
e.Log.Errorf("fromAddress:%s, tokenAddress:%s, db error:%s", datas[i].Address, token.TokenAddress, err)
continue
}
if count > 0 {
e.Log.Errorf("fromAddress:%s, tokenAddress:%s, 交易中暂时不自动转账", datas[i].Address, token.TokenAddress)
continue
}
switch {
case token.TransType == 1 && token.TransValue.Cmp(decimal.Zero) > 0:
amount = amount.Mul(token.TransValue.Div(decimal.NewFromInt(100))).Truncate(int32(token.Decimals))
case token.TransType == 2 && token.TransValue.Cmp(decimal.Zero) > 0:
amount = token.TransValue.Truncate(int32(token.Decimals))
}
//todo: 自动转账
transferItems = append(transferItems, models.WmTransferItem{
IsAuto: 1,
NetworkId: network.Id,
TokenAddress: token.TokenAddress,
FromAddress: datas[i].Address,
ToAddress: network.ReceiveAddress,
Amount: amount,
Decimals: token.Decimals,
Type: token.TransType,
TypeValue: token.TransValue,
PrivateKey: datas[i].PrivateKey,
Status: 0,
Remark: "自动转账",
})
}
}
if len(transferItems) > 0 {
if err := e.Orm.CreateInBatches(transferItems, 100).Error; err != nil {
e.Log.Errorf("定时转账保存数据库失败 error:%s", err)
return err
}
transService := WmTransfer{Service: e.Service}
if err := transService.RunTransferJob(transferItems); err != nil {
e.Log.Errorf("定时转账执行失败 error:%s", err)
} }
} }
return nil return nil
} }
// ClearAll 清空表数据
func (e *WmWalletInfo) ClearAll() error {
err := e.Orm.Exec("TRUNCATE TABLE wm_wallet_info;").Error
return err
}

View File

@ -10,7 +10,9 @@ import (
// 字典 key 可以配置到 自动任务 调用目标 中; // 字典 key 可以配置到 自动任务 调用目标 中;
func InitJob() { func InitJob() {
jobList = map[string]JobExec{ jobList = map[string]JobExec{
"ExamplesOne": ExamplesOne{}, "ExamplesOne": ExamplesOne{},
"TransferStatusJob": TransferStatusJob{},
"TransferJob": TransferJob{},
// ... // ...
} }
} }

View File

@ -10,6 +10,8 @@ import (
type TransferJob struct{} type TransferJob struct{}
type TransferStatusJob struct{}
// 定期转账 // 定期转账
func (t TransferJob) Exec(arg interface{}) error { func (t TransferJob) Exec(arg interface{}) error {
walletService := service.WmWalletInfo{} walletService := service.WmWalletInfo{}
@ -29,3 +31,12 @@ func getDefaultDb() *gorm.DB {
} }
return db return db
} }
// 定时查询交易状态
func (t TransferStatusJob) Exec(arg interface{}) error {
walletService := service.WmTransfer{}
walletService.Orm = getDefaultDb()
walletService.Log = logger.NewHelper(logger.DefaultLogger)
return walletService.CheckHashStatus()
}

View File

@ -2,6 +2,7 @@ package jobs
import ( import (
"go-admin/config" "go-admin/config"
helper "go-admin/utils/redishelper"
"testing" "testing"
"github.com/go-admin-team/go-admin-core/sdk" "github.com/go-admin-team/go-admin-core/sdk"
@ -17,7 +18,23 @@ func TestTransferJob(t *testing.T) {
config.ExtConfig.ApiEndpoint = "https://stylish-cool-fire.ethereum-sepolia.quiknode.pro/17572db4c091accfa5dc6faa0c60a805e5173459" config.ExtConfig.ApiEndpoint = "https://stylish-cool-fire.ethereum-sepolia.quiknode.pro/17572db4c091accfa5dc6faa0c60a805e5173459"
config.ExtConfig.ProxyUrl = "http://127.0.0.1:7890" config.ExtConfig.ProxyUrl = "http://127.0.0.1:7890"
helper.InitDefaultRedis("http://127.0.0.1:6379", "", 15) // 初始化配置
job := TransferJob{} job := TransferJob{}
job.Exec(nil) job.Exec(nil)
} }
// 测试状态任务
func TestTransferStatusJob(t *testing.T) {
dsn := "root:123456@tcp(127.0.0.1:3306)/eth_transfer?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
sdk.Runtime.SetDb("default", db)
config.ExtConfig.ApiEndpoint = "https://stylish-cool-fire.ethereum-sepolia.quiknode.pro/17572db4c091accfa5dc6faa0c60a805e5173459"
config.ExtConfig.ProxyUrl = "http://127.0.0.1:7890"
job := TransferStatusJob{}
job.Exec(nil)
}

View File

@ -3,13 +3,15 @@ package api
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/go-admin-team/go-admin-core/sdk/runtime"
"log" "log"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"time" "time"
"github.com/go-admin-team/go-admin-core/logger"
"github.com/go-admin-team/go-admin-core/sdk/runtime"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-admin-team/go-admin-core/config/source/file" "github.com/go-admin-team/go-admin-core/config/source/file"
"github.com/go-admin-team/go-admin-core/sdk" "github.com/go-admin-team/go-admin-core/sdk"
@ -20,6 +22,7 @@ import (
"go-admin/app/admin/models" "go-admin/app/admin/models"
"go-admin/app/admin/router" "go-admin/app/admin/router"
"go-admin/app/admin/service"
"go-admin/app/jobs" "go-admin/app/jobs"
"go-admin/common/database" "go-admin/common/database"
"go-admin/common/global" "go-admin/common/global"
@ -27,6 +30,7 @@ import (
"go-admin/common/middleware/handler" "go-admin/common/middleware/handler"
"go-admin/common/storage" "go-admin/common/storage"
ext "go-admin/config" ext "go-admin/config"
helper "go-admin/utils/redishelper"
) )
var ( var (
@ -86,6 +90,16 @@ func run() error {
f() f()
} }
//初始化redis
helper.InitDefaultRedis(config.CacheConfig.Redis.Addr, config.CacheConfig.Redis.Password, config.CacheConfig.Redis.DB)
if err := helper.DefaultRedis.Ping(); err != nil {
fmt.Println("初始化redis失败")
os.Exit(2)
}
initCache()
srv := &http.Server{ srv := &http.Server{
Addr: fmt.Sprintf("%s:%d", config.ApplicationConfig.Host, config.ApplicationConfig.Port), Addr: fmt.Sprintf("%s:%d", config.ApplicationConfig.Host, config.ApplicationConfig.Port),
Handler: sdk.Runtime.GetEngine(), Handler: sdk.Runtime.GetEngine(),
@ -184,3 +198,17 @@ func initRouter() {
common.InitMiddleware(r) common.InitMiddleware(r)
} }
// 初始化缓存
func initCache() {
netWorkService := service.WmNetwork{}
dbs := sdk.Runtime.GetDb()
netWorkService.Orm = dbs["*"]
netWorkService.Log = logger.NewHelper(logger.DefaultLogger)
tokenService := service.WmToken{}
tokenService.Orm = netWorkService.Orm
tokenService.Log = netWorkService.Log
netWorkService.ReCache()
tokenService.ReCache("")
}

View File

@ -0,0 +1,8 @@
package rediskey
const (
//区块链网络列表 LIST
NetWorks = "network"
//代币信息 LIST {networkCode}
Tokens = "token:%v"
)

View File

@ -50,6 +50,7 @@ settings:
name: data name: data
apiEndpoint: https://stylish-cool-fire.ethereum-sepolia.quiknode.pro/17572db4c091accfa5dc6faa0c60a805e5173459 apiEndpoint: https://stylish-cool-fire.ethereum-sepolia.quiknode.pro/17572db4c091accfa5dc6faa0c60a805e5173459
proxyUrl: http://127.0.0.1:7890
cache: cache:
redis: redis:
addr: 127.0.0.1:6379 addr: 127.0.0.1:6379

14
go.mod
View File

@ -49,12 +49,14 @@ require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/bsm/redislock v0.5.0 // indirect github.com/bsm/redislock v0.5.0 // indirect
github.com/bytedance/sonic v1.9.1 // indirect github.com/bytedance/sonic v1.13.2 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/casbin/redis-watcher/v2 v2.0.0-20220614104201-0e70bf2be930 // indirect github.com/casbin/redis-watcher/v2 v2.0.0-20220614104201-0e70bf2be930 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chanxuehong/rand v0.0.0-20201110082127-2f19a1bdd973 // indirect github.com/chanxuehong/rand v0.0.0-20201110082127-2f19a1bdd973 // indirect
github.com/chanxuehong/wechat v0.0.0-20201110083048-0180211b69fd // indirect github.com/chanxuehong/wechat v0.0.0-20201110083048-0180211b69fd // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/consensys/bavard v0.1.27 // indirect github.com/consensys/bavard v0.1.27 // indirect
github.com/consensys/gnark-crypto v0.16.0 // indirect github.com/consensys/gnark-crypto v0.16.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
@ -133,6 +135,8 @@ require (
github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a // indirect github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a // indirect
github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect github.com/prometheus/procfs v0.7.3 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.4 // indirect
github.com/robinjoseph08/redisqueue/v2 v2.1.0 // indirect github.com/robinjoseph08/redisqueue/v2 v2.1.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shamsher31/goimgext v1.0.0 // indirect github.com/shamsher31/goimgext v1.0.0 // indirect
@ -141,19 +145,23 @@ require (
github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/supranational/blst v0.3.14 // indirect github.com/supranational/blst v0.3.14 // indirect
github.com/tiendc/go-deepcopy v1.6.0 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.6.1 // indirect
github.com/tsuyoshiwada/go-gitcmd v0.0.0-20180205145712-5f1f5f9475df // indirect github.com/tsuyoshiwada/go-gitcmd v0.0.0-20180205145712-5f1f5f9475df // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // 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.11 // indirect
github.com/urfave/cli v1.22.1 // indirect github.com/urfave/cli v1.22.1 // indirect
github.com/xuri/efp v0.0.1 // indirect
github.com/xuri/excelize/v2 v2.9.1 // indirect
github.com/xuri/nfp v0.0.1 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.uber.org/atomic v1.6.0 // indirect go.uber.org/atomic v1.6.0 // indirect
go.uber.org/multierr v1.5.0 // indirect go.uber.org/multierr v1.5.0 // indirect
go.uber.org/zap v1.15.0 // indirect go.uber.org/zap v1.15.0 // indirect
golang.org/x/arch v0.3.0 // indirect golang.org/x/arch v0.3.0 // indirect
golang.org/x/image v0.1.0 // indirect golang.org/x/image v0.25.0 // indirect
golang.org/x/net v0.36.0 // indirect golang.org/x/net v0.40.0 // indirect
golang.org/x/sync v0.14.0 // indirect golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect golang.org/x/text v0.25.0 // indirect

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"go-admin/abis"
"math/big" "math/big"
"net/http" "net/http"
"net/url" "net/url"
@ -38,7 +37,11 @@ const erc20ABI = `[
]` ]`
// GetERC20Balance 查询 ERC-20 代币余额并转换为正常单位 (使用 decimal) // GetERC20Balance 查询 ERC-20 代币余额并转换为正常单位 (使用 decimal)
func GetERC20Balance(client *ethclient.Client, tokenAbi abis.TokenABI, accountAddress string) (decimal.Decimal, error) { // tokenAddress: 代币合约地址
// accountAddress: 账户地址
// decimals: 代币精度
// 返回值: 代币余额 (decimal.Decimal)
func GetERC20Balance(client *ethclient.Client, tokenAddress, accountAddress string, decimals int) (decimal.Decimal, error) {
// 1. 解析 ABI // 1. 解析 ABI
contractABI, err := abi.JSON(strings.NewReader(erc20ABI)) contractABI, err := abi.JSON(strings.NewReader(erc20ABI))
if err != nil { if err != nil {
@ -51,7 +54,7 @@ func GetERC20Balance(client *ethclient.Client, tokenAbi abis.TokenABI, accountAd
return decimal.Zero, fmt.Errorf("构造 balanceOf 调用数据失败: %w", err) return decimal.Zero, fmt.Errorf("构造 balanceOf 调用数据失败: %w", err)
} }
address := common.HexToAddress(tokenAbi.TestAddress) address := common.HexToAddress(tokenAddress)
// 3. 执行 balanceOf 调用 // 3. 执行 balanceOf 调用
balanceResult, err := client.CallContract(context.Background(), ethereum.CallMsg{ balanceResult, err := client.CallContract(context.Background(), ethereum.CallMsg{
To: &address, To: &address,
@ -74,7 +77,7 @@ func GetERC20Balance(client *ethclient.Client, tokenAbi abis.TokenABI, accountAd
// 8. 转换为正常单位 (使用 decimal) // 8. 转换为正常单位 (使用 decimal)
balanceDecimal := decimal.NewFromBigInt(balance, 0) // Create decimal from big.Int balanceDecimal := decimal.NewFromBigInt(balance, 0) // Create decimal from big.Int
decimalFactor := decimal.NewFromInt(10).Pow(decimal.NewFromInt(int64(tokenAbi.Decimals))) decimalFactor := decimal.NewFromInt(10).Pow(decimal.NewFromInt(int64(decimals)))
readableBalance := balanceDecimal.Div(decimalFactor) readableBalance := balanceDecimal.Div(decimalFactor)
return readableBalance, nil return readableBalance, nil

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"regexp"
"strings" "strings"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
@ -13,21 +14,114 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/shopspring/decimal"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
// TransferErc20 发送 ERC-20 代币交易。
// tokenaddress: ERC-20 代币的合约地址。 如果为空则转移ETH
func TransferErc20(
client *ethclient.Client,
fromPrivateKey string,
tokenAddress string,
toAddress string,
tokenAmount decimal.Decimal,
tokenDecimals uint8) (*types.Transaction, error) {
switch tokenAddress {
case "":
return TransferEth(client, fromPrivateKey, toAddress, tokenAmount)
default:
return TransferErc20Token(client, fromPrivateKey, tokenAddress, toAddress, tokenAmount, tokenDecimals)
}
}
// TransferEth 发送 ETH 交易。
func TransferEth(client *ethclient.Client, fromPrivateKey string, toAddress string, tokenAmount decimal.Decimal) (*types.Transaction, error) {
// 1. 解析私钥
privateKey, fromAddressCommon, err := GetAddressFromPrivateKey(fromPrivateKey)
if err != nil {
return nil, err
}
// 3. 获取发送者的 nonce交易序号
nonce, err := client.PendingNonceAt(context.Background(), fromAddressCommon)
if err != nil {
return nil, fmt.Errorf("获取 nonce 失败: %w", err)
}
// 4. 设置交易的 value (对于代币转账value 是 0)
value, _ := convertDecimalToBigInt(tokenAmount, 18)
// 5. 获取 Gas 价格
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
return nil, fmt.Errorf("获取 Gas 价格失败: %w", err)
}
// 6. 将地址字符串转换为 common.Address 类型
toAddressCommon := common.HexToAddress(toAddress)
// 7. 构造 ERC-20 transfer 函数的调用数据
// 7.1 函数签名transfer(address,uint256)
transferFnSignature := "transfer(address,uint256)" // 已经是标准化的
hash := sha3.NewLegacyKeccak256() // 或 sha3.NewLegacyKeccak256()
hash.Write([]byte(transferFnSignature))
// 7.4 拼接调用数据
var data []byte
// 8. 估算 Gas 消耗
gasLimit, err := client.EstimateGas(context.Background(), ethereum.CallMsg{
From: fromAddressCommon,
To: &toAddressCommon,
Data: data,
})
if err != nil {
return nil, fmt.Errorf("估算 Gas 消耗失败: %w", err)
}
// 9. (预估gas+基础费用)*1.1 作为 GasLimit
gasLimit = (gasLimit + 21000) * 12 / 10 // 增加 20%
if gasLimit < 23000 {
gasLimit = 23000 // 最小 Gas 限制
}
// 10. 创建交易
tx := types.NewTransaction(nonce, toAddressCommon, value, gasLimit, gasPrice, data)
// 11. 获取链 ID
chainID, err := client.NetworkID(context.Background())
if err != nil {
return nil, fmt.Errorf("获取链 ID 失败: %w", err)
}
// 12. 签名交易
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
return nil, fmt.Errorf("签名交易失败: %w", err)
}
// 13. 发送交易
err = client.SendTransaction(context.Background(), signedTx)
if err != nil {
return nil, fmt.Errorf("发送交易失败: %w", err)
}
return signedTx, nil
}
// transferErc20Token 发送 ERC-20 代币交易。 // transferErc20Token 发送 ERC-20 代币交易。
// tokenAmount: 要发送的代币数量 (例如1.0 代表 1 个代币)。 // tokenAmount: 要发送的代币数量 (例如1.0 代表 1 个代币)。
// fromPrivateKey: 发送者的私钥。 // fromPrivateKey: 发送者的私钥。
// tokenAddress: ERC-20 代币的合约地址。 // tokenAddress: ERC-20 代币的合约地址。
// toAddress: 接收者的地址。 // toAddress: 接收者的地址。
// tokenDecimals: 代币的小数位数 (例如USDC 是 6很多其他代币是 18)。 // tokenDecimals: 代币的小数位数 (例如USDC 是 6很多其他代币是 18)。
func transferErc20Token( func TransferErc20Token(
client *ethclient.Client, client *ethclient.Client,
fromPrivateKey string, fromPrivateKey string,
tokenAddress string, tokenAddress string,
toAddress string, toAddress string,
tokenAmount float64, tokenAmount decimal.Decimal,
tokenDecimals uint8, tokenDecimals uint8,
) (*types.Transaction, error) { ) (*types.Transaction, error) {
// 1. 解析私钥 // 1. 解析私钥
@ -57,15 +151,15 @@ func transferErc20Token(
// 7. 构造 ERC-20 transfer 函数的调用数据 // 7. 构造 ERC-20 transfer 函数的调用数据
// 7.1 函数签名transfer(address,uint256) // 7.1 函数签名transfer(address,uint256)
transferFnSignature := []byte("transfer(address,uint256)") transferFnSignature := "transfer(address,uint256)" // 已经是标准化的
hash := sha3.New256() hash := sha3.NewLegacyKeccak256() // 或 sha3.NewLegacyKeccak256()
hash.Write(transferFnSignature) hash.Write([]byte(transferFnSignature))
methodID := hash.Sum(nil)[:4] // 取前 4 个字节作为方法 ID methodID := hash.Sum(nil)[:4]
// 7.2 填充接收者地址和转账金额 // 7.2 填充接收者地址和转账金额
paddedAddress := common.LeftPadBytes(toAddressCommon.Bytes(), 32) paddedAddress := common.LeftPadBytes(toAddressCommon.Bytes(), 32)
// 7.3 将代币数量转换为最小单位 // 7.3 将代币数量转换为最小单位
amountBigInt, err := convertTokenAmountToBigInt(tokenAmount, tokenDecimals) amountBigInt, err := convertDecimalToBigInt(tokenAmount, tokenDecimals)
if err != nil { if err != nil {
return nil, fmt.Errorf("转换代币数量失败: %w", err) return nil, fmt.Errorf("转换代币数量失败: %w", err)
} }
@ -80,15 +174,15 @@ func transferErc20Token(
// 8. 估算 Gas 消耗 // 8. 估算 Gas 消耗
gasLimit, err := client.EstimateGas(context.Background(), ethereum.CallMsg{ gasLimit, err := client.EstimateGas(context.Background(), ethereum.CallMsg{
From: fromAddressCommon, From: fromAddressCommon,
To: &tokenAddressCommon, To: &toAddressCommon,
Data: data, Data: data,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("估算 Gas 消耗失败: %w", err) return nil, fmt.Errorf("估算 Gas 消耗失败: %w", err)
} }
// 9. 增加 Gas 限制的安全边际 // 9. (预估gas+基础费用)*1.1 作为 GasLimit
gasLimit = gasLimit * 11 / 10 // 增加 10% gasLimit = (gasLimit + 21000) * 12 / 10 // 增加 20%
if gasLimit < 23000 { if gasLimit < 23000 {
gasLimit = 23000 // 最小 Gas 限制 gasLimit = 23000 // 最小 Gas 限制
} }
@ -140,36 +234,45 @@ func GetAddressFromPrivateKey(fromPrivateKey string) (*ecdsa.PrivateKey, common.
} }
// convertTokenAmountToBigInt 将代币数量 (例如1.0) 转换为最小单位的整数表示 (例如USDC 的 1000000)。 // convertTokenAmountToBigInt 将代币数量 (例如1.0) 转换为最小单位的整数表示 (例如USDC 的 1000000)。
func convertTokenAmountToBigInt(tokenAmount float64, tokenDecimals uint8) (*big.Int, error) { func convertDecimalToBigInt(amountDecimal decimal.Decimal, tokenDecimals uint8) (*big.Int, error) {
// 1. 使用最大精度格式化浮点数 // 1. 创建一个与 token decimals 精度相同的 10 的幂
amountStr := fmt.Sprintf("%.18f", tokenAmount) // 使用最大精度 exponent := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(tokenDecimals)), nil)
// 2. 找到小数点的位置 // 2. 将 decimal 乘以该 10 的幂
decimalPointIndex := strings.Index(amountStr, ".") amountScaled := amountDecimal.Mul(decimal.NewFromBigInt(exponent, 0))
// 3. 如果没有小数点,则添加足够的 0 // 3. 将 scaled decimal 转换为 big.Int
if decimalPointIndex == -1 { amountBigInt := amountScaled.BigInt()
amountStr += "." + strings.Repeat("0", int(tokenDecimals))
} else {
// 4. 计算需要填充的 0 的数量
paddingNeeded := int(tokenDecimals) - (len(amountStr) - decimalPointIndex - 1)
// 5. 填充 0 或截断多余的小数位
if paddingNeeded > 0 {
amountStr += strings.Repeat("0", paddingNeeded)
} else if paddingNeeded < 0 {
amountStr = amountStr[:decimalPointIndex+int(tokenDecimals)+1]
}
// 6. 移除小数点
amountStr = strings.ReplaceAll(amountStr, ".", "")
}
// 7. 将字符串转换为 big.Int
amountBigInt := new(big.Int)
amountBigInt, ok := amountBigInt.SetString(amountStr, 10)
if !ok {
return nil, fmt.Errorf("将金额字符串转换为 big.Int 失败: %s", amountStr)
}
return amountBigInt, nil return amountBigInt, nil
} }
// GetTransactionByHash 获取交易的状态。
// status 0:失败 1-成功
// error: 交易未确认或不存在
func GetTransactionByHash(client *ethclient.Client, hash string) (int, error) {
txHash := common.HexToHash(hash)
// 获取交易收据(包含状态)
receipt, err := client.TransactionReceipt(context.Background(), txHash)
if err != nil {
return 0, errors.New("交易未确认或不存在")
}
return int(receipt.Status), nil
}
// 校验钱包地址是否合法
func IsValidAddress(address string) bool {
if !strings.HasPrefix(address, "0x") {
address = "0x" + address
}
if len(address) != 42 {
return false
}
if !regexp.MustCompile(`^0x[0-9a-fA-F]{40}$`).MatchString(address) {
return false
}
checksumAddress := common.HexToAddress(address).String()
return address == checksumAddress
}

View File

@ -0,0 +1,169 @@
package excelhelper
import (
"encoding/csv"
"errors"
"fmt"
helper "go-admin/utils"
"log"
"reflect"
"strings"
"github.com/xuri/excelize/v2"
"github.com/gin-gonic/gin"
)
/*
导出csv文件
- @fileName 文件名 不带拓展名
- @header 文件头
- @records 内容
*/
func ExportCSV(c *gin.Context, fileName string, header []string, records [][]string) error {
disposition := fmt.Sprintf("attachment; filename=%s.csv", fileName)
// Set headers
c.Header("Content-Description", "File Transfer")
c.Header("Content-Disposition", disposition)
c.Header("Content-Type", "text/csv")
// Create a CSV writer using the response writer
writer := csv.NewWriter(c.Writer)
defer writer.Flush()
// Write CSV header
writer.Write(header)
for _, record := range records {
writer.Write(record)
}
return nil
}
/*
导出excel
- @fileName 文件名称
- @data 数据源
- @ingore 忽略header
*/
func ExportExcel[T any](c *gin.Context, fileName string, data []T, ingore []string) error {
if len(data) == 0 {
return errors.New("无导出记录")
}
// Create a new Excel file
f := excelize.NewFile()
// Use reflection to get the header from struct tags
t := reflect.TypeOf(data[0])
headers := []string{}
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
excelTag := field.Tag.Get("excel")
if excelTag != "" && !helper.ArrayAny(ingore, excelTag) {
headers = append(headers, excelTag)
}
}
// Set headers
for i, header := range headers {
col := string('A' + i)
cell := fmt.Sprintf("%s1", col)
f.SetCellValue("Sheet1", cell, header)
}
// Fill rows with data
for rowIndex, item := range data {
rowValue := reflect.ValueOf(item)
rowType := rowValue.Type()
for colIndex, header := range headers {
col := string('A' + colIndex)
cell := fmt.Sprintf("%s%d", col, rowIndex+2)
var fieldValue reflect.Value
for i := 0; i < rowType.NumField(); i++ {
field := rowType.Field(i)
if strings.EqualFold(field.Tag.Get("excel"), header) {
fieldValue = rowValue.Field(i)
break
}
}
// Check if the fieldValue is valid before accessing it
if fieldValue.IsValid() && fieldValue.CanInterface() {
//f.SetCellValue("Sheet1", cell, fieldValue.Interface())
value := fieldValue.Interface()
// Ensure the value is a string, convert it if necessary
var stringValue string
if v, ok := value.(string); ok {
stringValue = v // If it's a string, use it directly
} else {
stringValue = fmt.Sprintf("%v", value) // Otherwise, convert to string
}
f.SetCellValue("Sheet1", cell, stringValue)
} else {
// Handle the case where fieldValue is invalid or nil
f.SetCellValue("Sheet1", cell, "")
}
}
}
// Set response headers and send the file to the client
// c.Writer.Header().Set("Content-Disposition", "attachment; filename=test.xlsx")
// c.Writer.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=binary")
// c.Writer.Header().Set("Content-Transfer-Encoding", "binary")
// c.Writer.Header().Set("Expires", "0")
// c.Writer.Header().Set("Cache-Control", "must-revalidate")
// c.Writer.Header().Set("Pragma", "public")
c.Header("Content-Description", "File Transfer")
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s.xlsx", fileName))
c.Header("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
c.Header("Content-Transfer-Encoding", "binary")
c.Header("Expires", "0")
c.Header("Cache-Control", "must-revalidate")
c.Header("Pragma", "public")
c.Header("Content-Encoding", "")
//fmt.Println("c.Writer.Header():", c.Writer.Header())
if _, err := f.WriteTo(c.Writer); err != nil {
log.Println("Error writing file:", err)
return err
}
return nil
//return f.WriteTo(c.Writer)
}
func MapExcelToStruct[T any](rows [][]string, headers []string) ([]T, error) {
var results []T
if len(rows) == 0 {
return results, nil
}
for _, row := range rows {
var result T
v := reflect.ValueOf(&result).Elem()
for i, header := range headers {
fieldName := ""
for j := 0; j < v.NumField(); j++ {
field := v.Type().Field(j)
tag := field.Tag.Get("excel")
if strings.EqualFold(tag, header) {
fieldName = field.Name
break
}
}
if fieldName != "" && i < len(row) {
field := v.FieldByName(fieldName)
if field.IsValid() && field.CanSet() {
field.Set(reflect.ValueOf(row[i]).Convert(field.Type()))
}
}
}
results = append(results, result)
}
return results, nil
}

52
utils/extension_helper.go Normal file
View File

@ -0,0 +1,52 @@
package helper
/*
判断是否存在
- @arr 数组
- @value 值
*/
func ArrayAny[T comparable](arr []T, value T) bool {
for _, v := range arr {
if v == value {
return true
}
}
return false
}
// 定义一个条件函数类型
type ConditionFunc[T any] func(T) bool
/*
判断是否存在
- @arr 数组
- @condition 判断函数
@return 对象指针
*/
func ArrayAnyExtension[T any](arr *[]T, condition ConditionFunc[T]) *T {
for _, v := range *arr {
if condition(v) {
return &v
}
}
return nil
}
func RemoveDuplicates(nums []int64) []int64 {
m := make(map[int64]bool)
result := []int64{}
for _, num := range nums {
if !m[num] {
m[num] = true
result = append(result, num)
}
}
return result
}

View File

@ -0,0 +1,779 @@
package helper
import (
"context"
"errors"
"fmt"
"log"
"math"
"reflect"
"strconv"
"time"
"github.com/bytedance/sonic"
"github.com/go-redis/redis/v8"
)
// RedisHelper 结构体封装了 Redis 客户端及上下文
type RedisHelper struct {
client *redis.Client // Redis 客户端
ctx context.Context // 上下文
emptyCacheValue string // 缓存空值的标志
}
var DefaultRedis *RedisHelper
// 初始化默认链接
func InitDefaultRedis(addr, password string, db int) {
if DefaultRedis == nil {
DefaultRedis = NewRedisHelper(addr, password, db)
}
log.Printf("初始化redis链接")
}
// NewRedisHelper 创建一个新的 RedisHelper 实例
func NewRedisHelper(addr, password string, db int) *RedisHelper {
rdb := redis.NewClient(&redis.Options{
Addr: addr, // Redis 服务器地址
Password: password, // Redis 密码
DB: db, // 使用的数据库编号
PoolSize: 50,
MinIdleConns: 10,
DialTimeout: 10 * time.Second, // 调整连接超时时间
ReadTimeout: 10 * time.Second, // 调整读超时时间
WriteTimeout: 10 * time.Second, // 调整写超时时间
})
return &RedisHelper{
client: rdb,
ctx: context.Background(), // 创建背景上下文
}
}
// 测试连接
func (r *RedisHelper) Ping() error {
return r.client.Ping(r.ctx).Err()
}
// SetString 设置字符串值
func (r *RedisHelper) SetString(key, value string) error {
return r.client.Set(r.ctx, key, value, 0).Err() // 将值存储到指定的键
}
// 批量设置
func (r *RedisHelper) BatchSet(maps *map[string]string) error {
pipe := r.client.Pipeline()
for key, val := range *maps {
pipe.Set(r.ctx, key, val, 0)
}
_, err := pipe.Exec(r.ctx)
return err
}
// SetString 设置字符串值
func (r *RedisHelper) SetStringExpire(key, value string, expireTime time.Duration) error {
return r.client.Set(r.ctx, key, value, expireTime).Err() // 将值存储到指定的键
}
// SetString 设置字符串值
func (r *RedisHelper) SetAdd(key, value string, expireTime time.Duration) error {
// 存储到 SET 中
result, err := r.client.SAdd(r.ctx, key, value).Result()
if err != nil {
return err
}
if result == 1 {
// 设置 SET 的过期时间
err = r.client.Expire(r.ctx, key, expireTime).Err()
if err != nil {
return errors.New("设置过期时间失败:" + err.Error())
}
return nil
} else {
return errors.New("key已存在")
}
}
// 设置对象
func SetObjString[T any](r *RedisHelper, key string, value T) error {
keyValue, err := sonic.Marshal(value)
if err != nil {
return err
}
return r.SetString(key, string(keyValue))
}
// 获取对象
func GetObjString[T any](r *RedisHelper, key string) (T, error) {
var result T
value, err := r.GetString(key)
if err != nil {
return result, err
}
err = sonic.Unmarshal([]byte(value), &result)
if err != nil {
return result, err
}
return result, nil
}
func (r *RedisHelper) Get(key string) *redis.StringCmd {
return r.client.Get(r.ctx, key)
}
/*
获取剩余时间
- @key redis key
*/
func (r *RedisHelper) TTL(key string) *redis.DurationCmd {
return r.client.TTL(r.ctx, key)
}
// GetString 获取字符串值
func (r *RedisHelper) GetString(key string) (string, error) {
return r.client.Get(r.ctx, key).Result() // 从指定的键获取值
}
// DeleteString 删除字符串键
func (r *RedisHelper) DeleteString(key string) error {
return r.client.Del(r.ctx, key).Err() // 删除指定的键
}
// DeleteString 删除目录下所有key
func (r *RedisHelper) DeleteAll(key string) error {
keys, err := r.ScanKeys(key)
if err != nil {
return err
}
_, err = r.BatchDeleteKeys(keys)
return err
}
/*
递增
- @key rediskey
*/
func (r *RedisHelper) Incr(key string) *redis.IntCmd {
return r.client.Incr(r.ctx, key)
}
/*
设置过期时间
- @key redis key
- @expiration 过期时间
*/
func (r *RedisHelper) Expire(key string, expiration time.Duration) *redis.BoolCmd {
return r.client.Expire(r.ctx, key, expiration)
}
/*
批量删除
- @keys 键数组
*/
func (r *RedisHelper) BatchDeleteKeys(keys []string) (int, error) {
if r.client == nil {
return 0, errors.New("Redis client is nil")
}
if len(keys) == 0 {
return 0, nil
}
deletedCount := 0
batchSize := 1000 // 每批次删除的键数量
for i := 0; i < len(keys); i += batchSize {
end := i + batchSize
if end > len(keys) {
end = len(keys)
}
batch := keys[i:end]
_, err := r.client.Pipelined(r.ctx, func(pipe redis.Pipeliner) error {
for _, key := range batch {
pipe.Del(r.ctx, key)
deletedCount++
}
return nil
})
if err != nil {
return deletedCount, fmt.Errorf("failed to delete keys in batch: %v", err)
}
}
return deletedCount, nil
}
// DeleteKeysByPrefix 删除指定前缀的键
func (r *RedisHelper) DeleteKeysByPrefix(prefixes ...string) error {
ctx := context.Background()
// 遍历每个前缀
for _, prefix := range prefixes {
var cursor uint64
var keys []string
// 使用 SCAN 命令查找匹配的键
for {
var err error
keys, cursor, err = r.client.Scan(ctx, cursor, prefix+"*", 1000).Result()
if err != nil {
return err
}
// 删除匹配的键
if len(keys) > 0 {
_, err := r.client.Del(ctx, keys...).Result()
if err != nil {
return err
}
fmt.Printf("Deleted keys with prefix '%s': %v\n", prefix, keys)
}
// 如果游标为 0表示迭代结束
if cursor == 0 {
break
}
}
}
return nil
}
// 查找所有value
func (r *RedisHelper) GetAllKeysAndValues(pattern string) ([]string, error) {
var cursor uint64
var result = []string{}
for {
// 使用 SCAN 命令获取匹配的键
keys, nextCursor, err := r.client.Scan(r.ctx, cursor, pattern+"*", 1000).Result()
if err != nil {
log.Printf("Error scanning keys: %v", err)
return nil, err
}
// 处理匹配到的键
for _, key := range keys {
value, err := r.client.Get(r.ctx, key).Result()
if err != nil {
if err == redis.Nil {
fmt.Printf("Key %s does not exist\n", key)
} else {
fmt.Printf("Error getting value for key %s: %v", key, err)
}
} else {
result = append(result, value)
}
}
// 如果 cursor 为 0表示扫描完成
if nextCursor == 0 {
break
}
cursor = nextCursor
}
return result, nil
}
// LPushList 将一个或多个值插入到列表的头部
func (r *RedisHelper) LPushList(key string, values ...string) error {
return r.client.LPush(r.ctx, key, values).Err() // 将值插入到列表的头部
}
// RPushList 将一个或多个值插入到列表的尾部
func (r *RedisHelper) RPushList(key string, values ...string) error {
return r.client.RPush(r.ctx, key, values).Err() // 将值插入到列表的尾部
}
// LPopList 从列表的头部弹出一个元素
func (r *RedisHelper) LPopList(key string) (string, error) {
return r.client.LPop(r.ctx, key).Result() // 从列表的头部移除并返回第一个元素
}
// RPopList 从列表的尾部弹出一个元素
func (r *RedisHelper) RPopList(key string) (string, error) {
return r.client.RPop(r.ctx, key).Result() // 从列表的尾部移除并返回最后一个元素
}
// LRangeList 获取列表中指定范围的元素
func (r *RedisHelper) LRangeList(key string, start, stop int64) ([]string, error) {
return r.client.LRange(r.ctx, key, start, stop).Result() // 获取列表中指定范围的元素
}
// GetAllList 获取列表中的所有元素
func (r *RedisHelper) GetAllList(key string) ([]string, error) {
values, err := r.client.LRange(r.ctx, key, 0, -1).Result()
if err == redis.Nil {
return nil, nil
} else if err != nil {
return nil, err
}
// 检查是否包含空值标志
if len(values) == 1 && values[0] == r.emptyCacheValue {
return nil, nil
}
return values, nil
}
func (r *RedisHelper) LRem(key, val string) (int64, error) {
count := 0 // 删除所有与 valueToRemove 相等的元素
result, err := r.client.LRem(r.ctx, key, int64(count), val).Result()
if err != nil {
fmt.Printf("删除元素失败: %v\n", err)
}
return result, nil
}
func (r *RedisHelper) IsElementInList(key string, element string) (bool, error) {
var cursor int64 = 0
const batchSize int64 = 1000 // 每批次获取的元素数量
for {
// 分批次获取列表元素
elements, err := r.client.LRange(r.ctx, key, cursor, cursor+batchSize-1).Result()
if err != nil {
return false, err
}
if len(elements) == 0 {
break // 没有更多数据
}
// 遍历当前批次的元素
for _, e := range elements {
if e == element {
return true, nil
}
}
cursor += batchSize // 移动到下一批次
}
return false, nil
}
/*
SetListCache 重新设置列表缓存
- @expiration 0-过期 1-过期时间
*/
func (r *RedisHelper) SetListCache(key string, expiration time.Duration, values ...string) error {
tempKey := key + ":temp"
// 使用事务来确保操作的原子性
pipe := r.client.TxPipeline()
// 将新数据插入到临时列表中
pipe.RPush(r.ctx, tempKey, values)
// 重命名临时列表为目标列表
pipe.Rename(r.ctx, tempKey, key)
if expiration > 0 {
// 设置目标列表的过期时间
pipe.Expire(r.ctx, key, expiration)
}
// 执行事务
_, err := pipe.Exec(r.ctx)
return err
}
// SetEmptyListCache 设置空值缓存
func (r *RedisHelper) SetEmptyListCache(key string, expiration time.Duration) error {
// 使用一个特殊标志值表示列表为空
_, err := r.client.RPush(r.ctx, key, r.emptyCacheValue).Result()
if err != nil {
return err
}
// 设置列表的过期时间
return r.client.Expire(r.ctx, key, expiration).Err()
}
// scanKeys 使用 SCAN 命令获取所有匹配的键
func (r *RedisHelper) ScanKeys(pattern string) ([]string, error) {
var cursor uint64
var keys []string
for {
var newKeys []string
var err error
// SCAN 命令每次返回部分匹配的键
newKeys, cursor, err = r.client.Scan(r.ctx, cursor, pattern, 1000).Result()
if err != nil {
return nil, err
}
keys = append(keys, newKeys...)
if cursor == 0 {
break
}
}
return keys, nil
}
// 泛型函数,用于获取所有键的列表数据并合并为一个数组
func GetAndMergeLists[T any](r *RedisHelper, keys []string) ([]T, error) {
var combinedList []T
for _, key := range keys {
// 获取每个键的列表数据
listData, err := r.client.LRange(r.ctx, key, 0, -1).Result()
if err != nil {
return nil, err
}
// 解码每个数据项为类型 T并添加到结果列表中
for _, data := range listData {
var item T
if err := sonic.Unmarshal([]byte(data), &item); err != nil {
return nil, err
}
combinedList = append(combinedList, item)
}
}
return combinedList, nil
}
// SetNX 实现类似于 Redis 的 SETNX 命令
func (r *RedisHelper) SetNX(key string, value interface{}, expiration time.Duration) (bool, error) {
result, err := r.client.Set(r.ctx, key, value, expiration).Result()
if err != nil {
return false, err
}
// 如果键不存在则 result 会等于 "OK"
return result == "OK", nil
}
func getFieldsFromStruct(obj interface{}) map[string]interface{} {
fields := make(map[string]interface{})
val := reflect.ValueOf(obj)
typ := reflect.TypeOf(obj)
for i := 0; i < val.NumField(); i++ {
field := typ.Field(i)
tag := field.Tag.Get("redis")
if tag != "" {
fieldVal := val.Field(i)
if fieldVal.Kind() == reflect.Slice || fieldVal.Kind() == reflect.Map {
// 处理切片或映射类型
// 对于切片,使用索引作为字段名
if fieldVal.Kind() == reflect.Slice {
for j := 0; j < fieldVal.Len(); j++ {
elem := fieldVal.Index(j).Interface()
fields[fmt.Sprintf("%s_%d", tag, j)] = elem
}
} else if fieldVal.Kind() == reflect.Map {
// 对于映射,使用键作为字段名
for _, key := range fieldVal.MapKeys() {
elem := fieldVal.MapIndex(key).Interface()
fields[fmt.Sprintf("%s_%v", tag, key.Interface())] = elem
}
}
} else {
fields[tag] = fieldVal.Interface()
}
}
}
return fields
}
func (r *RedisHelper) SetHashWithTags(key string, obj interface{}) error {
fields := getFieldsFromStruct(obj)
_, err := r.client.HSet(r.ctx, key, fields).Result()
return err
}
// HSetField 设置哈希中的一个字段
func (r *RedisHelper) HSetField(key, field string, value interface{}) error {
_, err := r.client.HSet(r.ctx, key, field, value).Result()
if err != nil {
log.Printf("Error setting field %s in hash %s: %v", field, key, err)
return err
}
return nil
}
// HSetMultipleFields 设置哈希中的多个字段
func (r *RedisHelper) HSetMultipleFields(key string, fields map[string]interface{}) error {
_, err := r.client.HSet(r.ctx, key, fields).Result()
if err != nil {
log.Printf("Error setting multiple fields in hash %s: %v", key, err)
return err
}
return nil
}
// HGetField 获取哈希中某个字段的值
func (r *RedisHelper) HGetField(key, field string) (string, error) {
val, err := r.client.HGet(r.ctx, key, field).Result()
if err != nil {
if err == redis.Nil {
return "", nil // Field does not exist
}
log.Printf("Error getting field %s from hash %s: %v", field, key, err)
return "", err
}
return val, nil
}
// HGetAllFields 获取哈希中所有字段的值
func (r *RedisHelper) HGetAllFields(key string) (map[string]string, error) {
fields, err := r.client.HGetAll(r.ctx, key).Result()
if err != nil {
log.Printf("Error getting all fields from hash %s: %v", key, err)
return nil, err
}
return fields, nil
}
// HDelField 删除哈希中的某个字段
func (r *RedisHelper) HDelField(key, field string) error {
_, err := r.client.HDel(r.ctx, key, field).Result()
if err != nil {
log.Printf("Error deleting field %s from hash %s: %v", field, key, err)
return err
}
return nil
}
// 删除哈希
func (r *RedisHelper) HDelAll(key string) error {
_, err := r.client.Del(r.ctx, key).Result()
if err != nil {
log.Printf("Error deleting from hash %s: %v", key, err)
return err
}
return nil
}
// HKeys 获取哈希中所有字段的名字
func (r *RedisHelper) HKeys(key string) ([]string, error) {
fields, err := r.client.HKeys(r.ctx, key).Result()
if err != nil {
log.Printf("Error getting keys from hash %s: %v", key, err)
return nil, err
}
return fields, nil
}
func (r *RedisHelper) HExists(key, field, value string) (bool, error) {
exists, err := r.client.HExists(r.ctx, key, field).Result()
if err != nil {
return false, fmt.Errorf("check existence failed: %v", err)
}
if !exists {
return false, nil
}
storedValue, err := r.client.HGet(r.ctx, key, field).Result()
if err != nil {
return false, fmt.Errorf("get value failed: %v", err)
}
// 如果值是 JSON比较前反序列化
var storedObj, inputObj interface{}
if err := sonic.UnmarshalString(storedValue, &storedObj); err != nil {
return false, fmt.Errorf("unmarshal stored value failed: %v", err)
}
if err := sonic.UnmarshalString(value, &inputObj); err != nil {
return false, fmt.Errorf("unmarshal input value failed: %v", err)
}
// 比较两个对象(需要根据实际类型调整)
return fmt.Sprintf("%v", storedObj) == fmt.Sprintf("%v", inputObj), nil
}
// DelSet 从集合中删除元素
func (r *RedisHelper) DelSet(key string, value string) error {
_, err := r.client.SRem(r.ctx, key, value).Result()
if err != nil {
log.Printf("Error del value from set %s: %v", key, err)
return err
}
return nil
}
func (r *RedisHelper) Sismember(key string, value string) (bool, error) {
result, err := r.client.SIsMember(r.ctx, key, value).Result()
if err != nil {
log.Printf("Error Sismember value from set %s: %v", key, err)
}
return result, err
}
// sort set start
// 批量添加
func (r *RedisHelper) BatchSortSet(key string, array []*redis.Z) error {
pipe := r.client.Pipeline()
for _, val := range array {
pipe.ZAdd(r.ctx, key, val)
}
_, err := pipe.Exec(r.ctx)
return err
}
// 单一写入 sort set
func (e *RedisHelper) SignelAdd(key string, score float64, member string) error {
// 先删除具有相同 score 的所有成员
scoreStr := strconv.FormatFloat(score, 'g', -1, 64)
_, err := e.client.ZRemRangeByScore(e.ctx, key, scoreStr, scoreStr).Result()
if err != nil {
fmt.Printf("删除score失败,err:%s", err.Error())
}
_, err = e.client.ZAdd(e.ctx, key, &redis.Z{
Score: score,
Member: member,
}).Result()
if err != nil {
return err
}
return nil
}
// 写入数据
func (e *RedisHelper) AddSortSet(key string, score float64, member string) error {
_, err := e.client.ZAdd(e.ctx, key, &redis.Z{
Score: score,
Member: member,
}).Result()
if err != nil {
return err
}
return nil
}
// 删除指定元素
func (e *RedisHelper) DelSortSet(key, member string) error {
return e.client.ZRem(e.ctx, key, member).Err()
}
// RemoveBeforeScore 移除 Sorted Set 中分数小于等于指定值的数据
// key: Sorted Set 的键
// score: 分数上限,所有小于等于此分数的元素将被移除
// 返回值: 移除的元素数量和可能的错误
func (e *RedisHelper) RemoveBeforeScore(key string, score float64) (int64, error) {
if key == "" {
return 0, errors.New("key 不能为空")
}
if math.IsNaN(score) || math.IsInf(score, 0) {
return 0, errors.New("score 必须是有效数字")
}
// 使用 ZRemRangeByScore 移除数据
count, err := e.client.ZRemRangeByScore(e.ctx, key, "-inf", strconv.FormatFloat(score, 'f', -1, 64)).Result()
if err != nil {
return 0, fmt.Errorf("移除 Sorted Set 数据失败, key: %s, score: %f, err: %v", key, score, err)
}
return count, nil
}
// GetNextAfterScore 获取指定分数及之后的第一条数据(包含指定分数)
func (e *RedisHelper) GetNextAfterScore(key string, score float64) (string, error) {
// 使用 ZRangeByScore 获取大于等于 score 的第一条数据
zs, err := e.client.ZRangeByScoreWithScores(e.ctx, key, &redis.ZRangeBy{
Min: fmt.Sprintf("%f", score), // 包含指定分数
Max: "+inf", // 上限为正无穷
Offset: 0, // 从第 0 条开始
Count: 1, // 只取 1 条
}).Result()
if err != nil {
return "", fmt.Errorf("获取数据失败: %v", err)
}
if len(zs) == 0 {
return "", nil // 没有符合条件的元素
}
return zs[0].Member.(string), nil
}
/*
获取sort set 所有数据
*/
func (e *RedisHelper) GetAllSortSet(key string) ([]string, error) {
return e.client.ZRange(e.ctx, key, 0, -1).Result()
}
/*
获取sort set 所有数据和score
*/
func (e *RedisHelper) GetRevRangeScoresSortSet(key string) ([]redis.Z, error) {
return e.client.ZRevRangeWithScores(e.ctx, key, 0, -1).Result()
}
// 获取最后一条数据
func (e *RedisHelper) GetLastSortSet(key string) ([]redis.Z, error) {
// 获取最后一个元素及其分数
results, err := e.client.ZRevRangeWithScores(e.ctx, key, 0, 0).Result()
if err != nil {
return nil, fmt.Errorf("failed to get last member: %w", err)
}
// 如果没有数据,返回空
if len(results) == 0 {
return []redis.Z{}, nil
}
return results, nil
}
// 获取指定区间数据
func (e *RedisHelper) GetSortSetMembers(key string, start, stop int64) ([]string, error) {
return e.client.ZRange(e.ctx, key, start, stop).Result()
}
// 获取最后N条数据
func (e *RedisHelper) GetLastSortSetMembers(key string, num int64) ([]string, error) {
return e.client.ZRevRange(e.ctx, key, 0, num).Result()
}
// func (e *RedisHelper) DelSortSet(key,)
// 根据索引范围删除
func (e *RedisHelper) DelByRank(key string, start, stop int64) error {
return e.client.ZRemRangeByRank(e.ctx, key, start, stop).Err()
}
// sort set end
// GetUserLoginPwdErrFre 获取用户登录密码错误频次
func (e *RedisHelper) GetUserLoginPwdErrFre(key string) (total int, wait time.Duration, err error) {
total, _ = e.client.Get(e.ctx, key).Int()
wait = e.client.TTL(e.ctx, key).Val()
return
}
// SetUserLoginPwdErrFre 设置用户登录密码错误频次
func (e *RedisHelper) SetUserLoginPwdErrFre(key string, expire time.Duration) (val int64, err error) {
val, err = e.client.Incr(e.ctx, key).Result()
if err != nil {
return
}
if err = e.client.Expire(e.ctx, key, expire).Err(); err != nil {
return
}
return
}