1、在线支付
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Build / build (push) Has been cancelled
				
			
		
			
				
	
				CodeQL / Analyze (go) (push) Has been cancelled
				
			
		
			
				
	
				build / Build (push) Has been cancelled
				
			
		
			
				
	
				GitHub Actions Mirror / mirror_to_gitee (push) Has been cancelled
				
			
		
			
				
	
				GitHub Actions Mirror / mirror_to_gitlab (push) Has been cancelled
				
			
		
			
				
	
				Issue Close Require / issue-close-require (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Build / build (push) Has been cancelled
				
			CodeQL / Analyze (go) (push) Has been cancelled
				
			build / Build (push) Has been cancelled
				
			GitHub Actions Mirror / mirror_to_gitee (push) Has been cancelled
				
			GitHub Actions Mirror / mirror_to_gitlab (push) Has been cancelled
				
			Issue Close Require / issue-close-require (push) Has been cancelled
				
			2、查询平台剩余字符数
This commit is contained in:
		| @ -116,6 +116,12 @@ func (e TmPlatform) Insert(c *gin.Context) { | |||||||
| 		e.Error(500, err, err.Error()) | 		e.Error(500, err, err.Error()) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if err1 := req.Validate(); err1 != nil { | ||||||
|  | 		e.Error(500, err1, err1.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// 设置创建人 | 	// 设置创建人 | ||||||
| 	req.SetCreateBy(user.GetUserId(c)) | 	req.SetCreateBy(user.GetUserId(c)) | ||||||
|  |  | ||||||
| @ -152,6 +158,12 @@ func (e TmPlatform) Update(c *gin.Context) { | |||||||
| 		e.Error(500, err, err.Error()) | 		e.Error(500, err, err.Error()) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if err1 := req.Validate(); err1 != nil { | ||||||
|  | 		e.Error(500, err1, err1.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	req.SetUpdateBy(user.GetUserId(c)) | 	req.SetUpdateBy(user.GetUserId(c)) | ||||||
| 	p := actions.GetPermissionFromContext(c) | 	p := actions.GetPermissionFromContext(c) | ||||||
|  |  | ||||||
|  | |||||||
| @ -190,3 +190,28 @@ func (e TmPlatformAccount) Delete(c *gin.Context) { | |||||||
| 	} | 	} | ||||||
| 	e.OK(req.GetId(), "删除成功") | 	e.OK(req.GetId(), "删除成功") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // checkRemain 查询剩余翻译条数 | ||||||
|  | func (e TmPlatformAccount) QueryRemain(c *gin.Context) { | ||||||
|  | 	req := dto.TmPlatformAccountQueryRemainReq{} | ||||||
|  | 	s := service.TmPlatformAccount{} | ||||||
|  | 	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) | ||||||
|  | 	err = s.QueryRemain(&req, p) | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Error(500, err, fmt.Sprintf("查询剩余翻译条数失败,\r\n失败信息 %s", err.Error())) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	e.OK(nil, "查询成功") | ||||||
|  | } | ||||||
|  | |||||||
| @ -60,19 +60,22 @@ func (e TmRechargeLog) CreateOrder(c *gin.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	userId := user.GetUserId(c) | ||||||
| 	apiKey := c.GetString("apiKey") | 	apiKey := c.GetString("apiKey") | ||||||
|  |  | ||||||
| 	if code := req.Validate(); code != statuscode.Success { | 	if code := req.Validate(); code != statuscode.Success { | ||||||
| 		e.Error(code, nil, statuscode.ErrorMessage[code]) | 		e.Error(code, nil, statuscode.ErrorMessage[code]) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	code := s.CreateOrder(&req, apiKey) | 	req.UserId = userId | ||||||
|  | 	resp := dto.CustomCreateOrderResp{} | ||||||
|  | 	code := s.CreateOrder(&req, &resp, apiKey) | ||||||
| 	if code != statuscode.Success { | 	if code != statuscode.Success { | ||||||
| 		e.OK(code, statuscode.ErrorMessage[code]) | 		e.Error(code, nil, statuscode.ErrorMessage[code]) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	e.OK(nil, "success") | 	e.OK(resp, "success") | ||||||
| } | } | ||||||
|  |  | ||||||
| // 后台充值 | // 后台充值 | ||||||
| @ -107,6 +110,29 @@ func (e TmRechargeLog) ManagerRecharge(c *gin.Context) { | |||||||
| 	e.OK(nil, "充值成功") | 	e.OK(nil, "充值成功") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // 后台扣除 | ||||||
|  | func (e TmRechargeLog) ManagerDeduct(c *gin.Context) { | ||||||
|  | 	s := service.TmRechargeLog{} | ||||||
|  | 	req := dto.TmRechargeManageDeductReq{} | ||||||
|  | 	err := e.MakeContext(c). | ||||||
|  | 		MakeOrm(). | ||||||
|  | 		Bind(&req). | ||||||
|  | 		MakeService(&s.Service). | ||||||
|  | 		Errors | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Error(500, err, "") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = s.ManagerDeduct(&req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Error(500, err, "") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.OK(nil, "扣除成功") | ||||||
|  | } | ||||||
|  |  | ||||||
| // 获取即将过期充值记录 | // 获取即将过期充值记录 | ||||||
| func (e TmMember) GetMemberAdvent(c *gin.Context) { | func (e TmMember) GetMemberAdvent(c *gin.Context) { | ||||||
| 	s := service.TmRechargeLog{} | 	s := service.TmRechargeLog{} | ||||||
|  | |||||||
| @ -42,7 +42,7 @@ func (e Translate) Translate(c *gin.Context) { | |||||||
|  |  | ||||||
| 	if code != statuscode.Success { | 	if code != statuscode.Success { | ||||||
| 		e.Logger.Error(err) | 		e.Logger.Error(err) | ||||||
| 		e.OK(code, statuscode.ErrorMessage[code]) | 		e.Error(code, nil, statuscode.ErrorMessage[code]) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | |||||||
| @ -9,13 +9,15 @@ import ( | |||||||
| type TmPlatform struct { | type TmPlatform struct { | ||||||
| 	models.Model | 	models.Model | ||||||
|  |  | ||||||
| 	Name        string          `json:"name" gorm:"type:varchar(20);comment:平台名称"` | 	Name           string          `json:"name" gorm:"type:varchar(20);comment:平台名称"` | ||||||
| 	ShowName    string          `json:"showName" gorm:"type:varchar(20);comment:展示名称"` | 	ShowName       string          `json:"showName" gorm:"type:varchar(20);comment:展示名称"` | ||||||
| 	ApiBaseUrl  string          `json:"apiBaseUrl" gorm:"type:varchar(500);comment:平台接口地址"` | 	ApiBaseUrl     string          `json:"apiBaseUrl" gorm:"type:varchar(500);comment:平台接口地址"` | ||||||
| 	Description string          `json:"description" gorm:"type:varchar(255);comment:描述"` | 	Description    string          `json:"description" gorm:"type:varchar(255);comment:描述"` | ||||||
| 	Code        string          `json:"code" gorm:"type:varchar(20);comment:平台编码(字典 tm_platform)"` | 	Code           string          `json:"code" gorm:"type:varchar(20);comment:平台编码(字典 tm_platform)"` | ||||||
| 	Character   int             `json:"character" gorm:"type:int;comment:字符数"` | 	Character      int             `json:"character" gorm:"type:int;comment:字符数"` | ||||||
| 	Price       decimal.Decimal `json:"price" gorm:"type:decimal(10,2);comment:单价"` | 	Price          decimal.Decimal `json:"price" gorm:"type:decimal(10,2);comment:单价"` | ||||||
|  | 	BlockChain     string          `json:"blockChain" gorm:"type:varchar(20);comment:区块链(全小写)"` | ||||||
|  | 	ReceiveAddress string          `json:"receiveAddress" gorm:"type:varchar(100);comment:接收地址"` | ||||||
| 	models.ModelTime | 	models.ModelTime | ||||||
| 	models.ControlBy | 	models.ControlBy | ||||||
| } | } | ||||||
|  | |||||||
| @ -26,8 +26,9 @@ func registerTmMemberRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl | |||||||
| 		r.DELETE("", api.Delete) | 		r.DELETE("", api.Delete) | ||||||
|  |  | ||||||
| 		// r.POST("recharge", actions.PermissionAction(), api.Recharge)  //字符充值 | 		// r.POST("recharge", actions.PermissionAction(), api.Recharge)  //字符充值 | ||||||
| 		r.POST("manager-recharge", actions.PermissionAction(), rechargeApi.ManagerRecharge) | 		r.POST("manager-recharge", actions.PermissionAction(), rechargeApi.ManagerRecharge) //手动充值 | ||||||
| 		r.PUT("status", actions.PermissionAction(), api.ChangeStatus) //状态变更 | 		r.PUT("manager-deduct", actions.PermissionAction(), rechargeApi.ManagerDeduct)      //手动扣除 | ||||||
|  | 		r.PUT("status", actions.PermissionAction(), api.ChangeStatus)                       //状态变更 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	r2 := v1.Group("/tm-member").Use(authMiddleware.MiddlewareFunc()) | 	r2 := v1.Group("/tm-member").Use(authMiddleware.MiddlewareFunc()) | ||||||
| @ -35,5 +36,7 @@ func registerTmMemberRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl | |||||||
| 		r2.GET("/api-key", api.GetMyApiKey) | 		r2.GET("/api-key", api.GetMyApiKey) | ||||||
| 		r2.GET("platforms", api.GetPlatforms) | 		r2.GET("platforms", api.GetPlatforms) | ||||||
| 		r2.GET("member-advent", api.GetMemberAdvent) //获取用户即将过期的充值信息 | 		r2.GET("member-advent", api.GetMemberAdvent) //获取用户即将过期的充值信息 | ||||||
|  |  | ||||||
|  | 		r2.POST("recharge", rechargeApi.CreateOrder) //用户发起充值 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -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,7 @@ func registerTmPlatformAccountRouter(v1 *gin.RouterGroup, authMiddleware *jwt.Gi | |||||||
| 		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.PUT("query-remain/:id", actions.PermissionAction(), api.QueryRemain) //查询平台剩余字符 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,9 +1,11 @@ | |||||||
| 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" | ||||||
|  | 	"go-admin/utils/chainhelper" | ||||||
|  |  | ||||||
| 	"github.com/shopspring/decimal" | 	"github.com/shopspring/decimal" | ||||||
| ) | ) | ||||||
| @ -40,17 +42,43 @@ func (m *TmPlatformGetPageReq) GetNeedSearch() interface{} { | |||||||
| } | } | ||||||
|  |  | ||||||
| type TmPlatformInsertReq struct { | type TmPlatformInsertReq struct { | ||||||
| 	Id          int             `json:"-" comment:"平台id"` // 平台id | 	Id             int             `json:"-" comment:"平台id"` // 平台id | ||||||
| 	Name        string          `json:"name" comment:"平台名称"` | 	Name           string          `json:"name" comment:"平台名称"` | ||||||
| 	ShowName    string          `json:"showName" comment:"展示名称"` | 	ShowName       string          `json:"showName" comment:"展示名称"` | ||||||
| 	ApiBaseUrl  string          `json:"apiBaseUrl" comment:"平台接口地址"` | 	ApiBaseUrl     string          `json:"apiBaseUrl" comment:"平台接口地址"` | ||||||
| 	Description string          `json:"description" comment:"描述"` | 	Description    string          `json:"description" comment:"描述"` | ||||||
| 	Code        string          `json:"code" comment:"平台编码(字典 tm_platform)"` | 	Code           string          `json:"code" comment:"平台编码(字典 tm_platform)"` | ||||||
| 	Character   int             `json:"character" comment:"字符数"` | 	Character      int             `json:"character" comment:"字符数"` | ||||||
| 	Price       decimal.Decimal `json:"price" comment:"单价"` | 	Price          decimal.Decimal `json:"price" comment:"单价"` | ||||||
|  | 	BlockChain     string          `json:"blockChain" comment:"链区块"` | ||||||
|  | 	ReceiveAddress string          `json:"receiveAddress" comment:"接收地址"` | ||||||
| 	common.ControlBy | 	common.ControlBy | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (s *TmPlatformInsertReq) Validate() error { | ||||||
|  | 	if s.BlockChain == "" { | ||||||
|  | 		return errors.New("收款区块不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if s.ReceiveAddress == "" { | ||||||
|  | 		return errors.New("接收地址不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if s.Name == "" { | ||||||
|  | 		return errors.New("平台名称不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if s.ShowName == "" { | ||||||
|  | 		return errors.New("展示名称不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if s.Code == "" { | ||||||
|  | 		return errors.New("平台编码不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return chainhelper.JudgeChainAddress(s.BlockChain, s.ReceiveAddress) | ||||||
|  | } | ||||||
|  |  | ||||||
| func (s *TmPlatformInsertReq) Generate(model *models.TmPlatform) { | func (s *TmPlatformInsertReq) Generate(model *models.TmPlatform) { | ||||||
| 	if s.Id == 0 { | 	if s.Id == 0 { | ||||||
| 		model.Model = common.Model{Id: s.Id} | 		model.Model = common.Model{Id: s.Id} | ||||||
| @ -63,6 +91,8 @@ func (s *TmPlatformInsertReq) Generate(model *models.TmPlatform) { | |||||||
| 	model.Character = s.Character | 	model.Character = s.Character | ||||||
| 	model.Price = s.Price | 	model.Price = s.Price | ||||||
| 	model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 | 	model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 | ||||||
|  | 	model.BlockChain = s.BlockChain | ||||||
|  | 	model.ReceiveAddress = s.ReceiveAddress | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *TmPlatformInsertReq) GetId() interface{} { | func (s *TmPlatformInsertReq) GetId() interface{} { | ||||||
| @ -77,17 +107,43 @@ type TmPlatformListResp struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type TmPlatformUpdateReq struct { | type TmPlatformUpdateReq struct { | ||||||
| 	Id          int             `uri:"id" comment:"平台id"` // 平台id | 	Id             int             `uri:"id" comment:"平台id"` // 平台id | ||||||
| 	Name        string          `json:"name" comment:"平台名称"` | 	Name           string          `json:"name" comment:"平台名称"` | ||||||
| 	ShowName    string          `json:"showName" comment:"展示名称"` | 	ShowName       string          `json:"showName" comment:"展示名称"` | ||||||
| 	ApiBaseUrl  string          `json:"apiBaseUrl" comment:"平台接口地址"` | 	ApiBaseUrl     string          `json:"apiBaseUrl" comment:"平台接口地址"` | ||||||
| 	Description string          `json:"description" comment:"描述"` | 	Description    string          `json:"description" comment:"描述"` | ||||||
| 	Code        string          `json:"code" comment:"平台编码(字典 tm_platform)"` | 	Code           string          `json:"code" comment:"平台编码(字典 tm_platform)"` | ||||||
| 	Character   int             `json:"character" comment:"字符数"` | 	Character      int             `json:"character" comment:"字符数"` | ||||||
| 	Price       decimal.Decimal `json:"price" comment:"单价"` | 	Price          decimal.Decimal `json:"price" comment:"单价"` | ||||||
|  | 	BlockChain     string          `json:"blockChain" comment:"链区块"` | ||||||
|  | 	ReceiveAddress string          `json:"receiveAddress" comment:"接收地址"` | ||||||
| 	common.ControlBy | 	common.ControlBy | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (s *TmPlatformUpdateReq) Validate() error { | ||||||
|  | 	if s.BlockChain == "" { | ||||||
|  | 		return errors.New("收款区块不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if s.ReceiveAddress == "" { | ||||||
|  | 		return errors.New("接收地址不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if s.Name == "" { | ||||||
|  | 		return errors.New("平台名称不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if s.ShowName == "" { | ||||||
|  | 		return errors.New("展示名称不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if s.Code == "" { | ||||||
|  | 		return errors.New("平台编码不能为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return chainhelper.JudgeChainAddress(s.BlockChain, s.ReceiveAddress) | ||||||
|  | } | ||||||
|  |  | ||||||
| func (s *TmPlatformUpdateReq) Generate(model *models.TmPlatform) { | func (s *TmPlatformUpdateReq) Generate(model *models.TmPlatform) { | ||||||
| 	if s.Id == 0 { | 	if s.Id == 0 { | ||||||
| 		model.Model = common.Model{Id: s.Id} | 		model.Model = common.Model{Id: s.Id} | ||||||
| @ -100,6 +156,8 @@ func (s *TmPlatformUpdateReq) Generate(model *models.TmPlatform) { | |||||||
| 	model.Character = s.Character | 	model.Character = s.Character | ||||||
| 	model.Price = s.Price | 	model.Price = s.Price | ||||||
| 	model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 | 	model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 | ||||||
|  | 	model.BlockChain = s.BlockChain | ||||||
|  | 	model.ReceiveAddress = s.ReceiveAddress | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *TmPlatformUpdateReq) GetId() interface{} { | func (s *TmPlatformUpdateReq) GetId() interface{} { | ||||||
|  | |||||||
| @ -95,3 +95,7 @@ type TmPlatformAccountDeleteReq struct { | |||||||
| func (s *TmPlatformAccountDeleteReq) GetId() interface{} { | func (s *TmPlatformAccountDeleteReq) GetId() interface{} { | ||||||
| 	return s.Ids | 	return s.Ids | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type TmPlatformAccountQueryRemainReq struct { | ||||||
|  | 	Id int `uri:"id"` | ||||||
|  | } | ||||||
|  | |||||||
| @ -109,16 +109,45 @@ type TmRechargeCreateOrderReq struct { | |||||||
| 	common.ControlBy | 	common.ControlBy | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // 后台扣除字符 | ||||||
|  | type TmRechargeManageDeductReq struct { | ||||||
|  | 	MemberId   int             `json:"memberId" comment:"会员id"` | ||||||
|  | 	UserId     int             `json:"userId" comment:"用户id"` | ||||||
|  | 	PlatformId int             `json:"platformId" comment:"平台id"` | ||||||
|  | 	TotalChars decimal.Decimal `json:"totalChars" comment:"扣减字符"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *TmRechargeManageDeductReq) Generate(entity *models.TmRechargeLog) error { | ||||||
|  | 	if entity == nil { | ||||||
|  | 		entity = &models.TmRechargeLog{} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if e.MemberId <= 0 { | ||||||
|  | 		return errors.New("会员不存在") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if e.PlatformId <= 0 { | ||||||
|  | 		return errors.New("平台不存在") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	entity.MemberId = e.MemberId | ||||||
|  | 	entity.UserId = e.UserId | ||||||
|  | 	entity.PlatformId = e.PlatformId | ||||||
|  | 	entity.Type = 3 | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // 用户充值订单创建请求参数 | // 用户充值订单创建请求参数 | ||||||
| type CustomCreateOrderReq struct { | type CustomCreateOrderReq struct { | ||||||
| 	UserId     int             `json:"userId" comment:"用户id"` | 	UserId     int `json:"userId" comment:"用户id"` | ||||||
| 	Amount     decimal.Decimal `json:"amount" comment:"充值金额"` | 	PlatformId int `json:"platformId" comment:"平台id"` | ||||||
| 	PlatformId int             `json:"platformId" comment:"平台id"` | 	Count      int `json:"count" comment:"购买数量"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (e *CustomCreateOrderReq) Validate() int { | func (e *CustomCreateOrderReq) Validate() int { | ||||||
| 	if e.Amount.Cmp(decimal.Zero) <= 0 { | 	if e.Count <= 0 { | ||||||
| 		return statuscode.RechargeAmountMustBeGreaterThanZero | 		return statuscode.RechargeNumberMustBeGreaterThanZero | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if e.PlatformId <= 0 { | 	if e.PlatformId <= 0 { | ||||||
| @ -128,6 +157,14 @@ func (e *CustomCreateOrderReq) Validate() int { | |||||||
| 	return statuscode.Success | 	return statuscode.Success | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type CustomCreateOrderResp struct { | ||||||
|  | 	OrderNo        string `json:"orderNo"` | ||||||
|  | 	Amount         string `json:"amount"` | ||||||
|  | 	BlockChain     string `json:"blockChain"` | ||||||
|  | 	ReceiveAddress string `json:"receiveAddress"` | ||||||
|  | 	ExpireUnix     int64  `json:"expireUnix" comment:"过期时间戳 秒"` | ||||||
|  | } | ||||||
|  |  | ||||||
| func (e *TmRechargeCreateOrderReq) Validate() error { | func (e *TmRechargeCreateOrderReq) Validate() error { | ||||||
| 	if e.ExpireDays <= 0 { | 	if e.ExpireDays <= 0 { | ||||||
| 		return errors.New("过期天数必须大于0") | 		return errors.New("过期天数必须大于0") | ||||||
|  | |||||||
| @ -21,6 +21,106 @@ type TmPlatformAccount struct { | |||||||
| 	service.Service | 	service.Service | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (e TmPlatformAccount) QueryRemain(req *dto.TmPlatformAccountQueryRemainReq, p *actions.DataPermission) error { | ||||||
|  | 	var entity models.TmPlatformAccount | ||||||
|  | 	err := e.Orm.Model(&entity). | ||||||
|  | 		Scopes( | ||||||
|  | 			actions.Permission(entity.TableName(), p), | ||||||
|  | 		). | ||||||
|  | 		First(&entity, req.Id).Error | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return errors.New("查看对象不存在或无权查看") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	platformService := TmPlatform{Service: e.Service} | ||||||
|  |  | ||||||
|  | 	platform, err := platformService.GetById(entity.PlatformId) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("获取翻译平台消息失败 %v", err) | ||||||
|  | 		return errors.New("获取翻译平台消息失败") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	provider := map[string]interface{}{ | ||||||
|  | 		entity.PlatformKey: map[string]interface{}{ | ||||||
|  | 			"apiKey":   entity.ApiKey, | ||||||
|  | 			"endpoint": platform.ApiBaseUrl, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	config := TranslatorServiceConfig{ | ||||||
|  | 		DefaultProvider: "deepseek", | ||||||
|  | 		ProviderConfigs: provider, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	translator, err := NewTranslatorService(&config) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("创建翻译服务失败 %v", err) | ||||||
|  | 		return errors.New("创建翻译服务失败") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	remainCount, err := translator.providers[entity.PlatformKey].GetRemainCount() | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		e.Log.Errorf("获取翻译平台剩余字符失败 %v", err) | ||||||
|  | 		return errors.New("获取翻译平台剩余字符失败") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if remainCount > 0 { | ||||||
|  | 		key := fmt.Sprintf(rediskey.TM_PLATEFORM_ACCOUNT_REMAIN_KEY, entity.PlatformKey, entity.ApiKey) | ||||||
|  |  | ||||||
|  | 		redishelper.DefaultRedis.SetString(key, strconv.Itoa(remainCount)) | ||||||
|  |  | ||||||
|  | 		//如果为禁用则写入队列 | ||||||
|  | 		if entity.Status == 2 { | ||||||
|  | 			listKey := fmt.Sprintf(rediskey.TM_PLATFORM_ACCOUNT_LIST_KEY, entity.PlatformKey) | ||||||
|  | 			vals, err := redishelper.DefaultRedis.GetAllList(listKey) | ||||||
|  |  | ||||||
|  | 			if err != nil { | ||||||
|  | 				e.Log.Errorf("获取redis列表失败 %v", err) | ||||||
|  | 				return errors.New("获取redis列表失败") | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			item := models.TmPlatformAccount{} | ||||||
|  | 			hasData := false | ||||||
|  |  | ||||||
|  | 			for _, val := range vals { | ||||||
|  | 				sonic.UnmarshalString(val, &item) | ||||||
|  |  | ||||||
|  | 				if item.ApiKey == entity.ApiKey { | ||||||
|  | 					hasData = true | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if !hasData { | ||||||
|  | 				entity.Status = 1 | ||||||
|  | 				val, err := sonic.MarshalString(entity) | ||||||
|  |  | ||||||
|  | 				if err != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				err = e.Orm.Transaction(func(tx *gorm.DB) error { | ||||||
|  | 					if err1 := redishelper.DefaultRedis.RPushList(listKey, val); err1 != nil { | ||||||
|  | 						return err | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					if err1 := tx.Model(entity).Update("status", 1).Error; err1 != nil { | ||||||
|  | 						return err1 | ||||||
|  | 					} | ||||||
|  | 					return nil | ||||||
|  | 				}) | ||||||
|  |  | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // GetPage 获取TmPlatformAccount列表 | // GetPage 获取TmPlatformAccount列表 | ||||||
| func (e *TmPlatformAccount) GetPage(c *dto.TmPlatformAccountGetPageReq, p *actions.DataPermission, list *[]models.TmPlatformAccount, count *int64) error { | func (e *TmPlatformAccount) GetPage(c *dto.TmPlatformAccountGetPageReq, p *actions.DataPermission, list *[]models.TmPlatformAccount, count *int64) error { | ||||||
| 	var err error | 	var err error | ||||||
| @ -38,6 +138,18 @@ func (e *TmPlatformAccount) GetPage(c *dto.TmPlatformAccountGetPageReq, p *actio | |||||||
| 		e.Log.Errorf("TmPlatformAccountService GetPage error:%s \r\n", err) | 		e.Log.Errorf("TmPlatformAccountService GetPage error:%s \r\n", err) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if list != nil { | ||||||
|  | 		for index, item := range *list { | ||||||
|  | 			keu := fmt.Sprintf(rediskey.TM_PLATEFORM_ACCOUNT_REMAIN_KEY, item.PlatformKey, item.ApiKey) | ||||||
|  | 			val, err := redishelper.DefaultRedis.GetString(keu) | ||||||
|  | 			if err != nil { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			(*list)[index].RemainChars, _ = strconv.Atoi(val) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -16,8 +16,10 @@ import ( | |||||||
| 	"go-admin/utils/utility" | 	"go-admin/utils/utility" | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"strconv" | 	"strconv" | ||||||
|  | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/bytedance/sonic" | ||||||
| 	"github.com/go-admin-team/go-admin-core/sdk/service" | 	"github.com/go-admin-team/go-admin-core/sdk/service" | ||||||
| 	"github.com/go-redis/redis/v8" | 	"github.com/go-redis/redis/v8" | ||||||
| 	"github.com/shopspring/decimal" | 	"github.com/shopspring/decimal" | ||||||
| @ -28,6 +30,26 @@ type TmRechargeLog struct { | |||||||
| 	service.Service | 	service.Service | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // 清理过期订单 | ||||||
|  | func (e TmRechargeLog) CleanExpiredOrder() error { | ||||||
|  | 	expireTime := time.Now().Add(5 * time.Minute) | ||||||
|  | 	ctx := context.Background() | ||||||
|  |  | ||||||
|  | 	err := e.Orm.Transaction(func(tx *gorm.DB) error { | ||||||
|  | 		if err1 := tx.Model(&models.TmRechargeLog{}).Where("order_expire_time <? and status =1", expireTime).Updates(map[string]interface{}{"status": 6, "updated_at": time.Now()}).Error; err1 != nil { | ||||||
|  | 			return err1 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if err1 := receiveaddressmanager.CleanExpiredAmountLocks(ctx, redishelper.DefaultRedis.GetClient()); err1 != nil { | ||||||
|  | 			return err1 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
| // 首页获取即将过期的数据 | // 首页获取即将过期的数据 | ||||||
| func (e *TmRechargeLog) GetMemberAdvent(req *dto.TmRechargeLogFrontReq, resp *[]dto.TmRechargeLogFrontResp, userId int) int { | func (e *TmRechargeLog) GetMemberAdvent(req *dto.TmRechargeLogFrontReq, resp *[]dto.TmRechargeLogFrontResp, userId int) int { | ||||||
| 	var datas []models.TmRechargeLog | 	var datas []models.TmRechargeLog | ||||||
| @ -68,7 +90,7 @@ func (e *TmRechargeLog) GetMemberAdvent(req *dto.TmRechargeLogFrontReq, resp *[] | |||||||
| func (e *TmRechargeLog) ManagerRecharge(req *dto.TmRechargeCreateOrderReq, p *actions.DataPermission) error { | func (e *TmRechargeLog) ManagerRecharge(req *dto.TmRechargeCreateOrderReq, p *actions.DataPermission) error { | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
| 	var data models.TmRechargeLog | 	var data models.TmRechargeLog | ||||||
| 	member, platform, memberPlatform, code := e.CreateOrderJudge(req) | 	member, platform, _, code := e.CreateOrderJudge(req.MemberId, req.PlatformId) | ||||||
| 	if code != statuscode.Success { | 	if code != statuscode.Success { | ||||||
| 		return errors.New(statuscode.ErrorMessage[code]) | 		return errors.New(statuscode.ErrorMessage[code]) | ||||||
| 	} | 	} | ||||||
| @ -79,6 +101,7 @@ func (e *TmRechargeLog) ManagerRecharge(req *dto.TmRechargeCreateOrderReq, p *ac | |||||||
| 	data.Type = 2 | 	data.Type = 2 | ||||||
| 	data.Status = 2 | 	data.Status = 2 | ||||||
| 	data.UserId = member.UserId | 	data.UserId = member.UserId | ||||||
|  | 	data.MemberId = member.Id | ||||||
| 	data.ExpireAt = time.Now().AddDate(0, 0, req.ExpireDays) | 	data.ExpireAt = time.Now().AddDate(0, 0, req.ExpireDays) | ||||||
| 	data.PayTime = &now | 	data.PayTime = &now | ||||||
|  |  | ||||||
| @ -96,10 +119,10 @@ func (e *TmRechargeLog) ManagerRecharge(req *dto.TmRechargeCreateOrderReq, p *ac | |||||||
| 			return err1 | 			return err1 | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		//更新用户翻译可用字符 | 		// //更新用户翻译可用字符 | ||||||
| 		if err1 := tx.Model(&models.TmMemberPlatform{}).Where("id =?", memberPlatform.Id).Update("remaining_character", data.TotalChars).Error; err1 != nil { | 		// if err1 := tx.Model(&models.TmMemberPlatform{}).Where("id =?", memberPlatform.Id).Update("remaining_character", data.TotalChars).Error; err1 != nil { | ||||||
| 			return err1 | 		// 	return err1 | ||||||
| 		} | 		// } | ||||||
|  |  | ||||||
| 		//写入可用字符 | 		//写入可用字符 | ||||||
| 		if err1 := qmgr.AddQuota(ctx, member.ApiKey, platform.Code, rechargeData); err1 != nil { | 		if err1 := qmgr.AddQuota(ctx, member.ApiKey, platform.Code, rechargeData); err1 != nil { | ||||||
| @ -112,11 +135,50 @@ func (e *TmRechargeLog) ManagerRecharge(req *dto.TmRechargeCreateOrderReq, p *ac | |||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // 后台扣除字符 | ||||||
|  | func (e *TmRechargeLog) ManagerDeduct(req *dto.TmRechargeManageDeductReq) error { | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 	var data models.TmRechargeLog | ||||||
|  |  | ||||||
|  | 	member, platform, _, code := e.CreateOrderJudge(req.MemberId, req.PlatformId) | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		return errors.New(statuscode.ErrorMessage[code]) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req.Generate(&data) | ||||||
|  | 	now := time.Now() | ||||||
|  | 	data.OrderNo = utility.GenerateTraceID() | ||||||
|  | 	data.Status = 2 | ||||||
|  | 	data.UserId = member.UserId | ||||||
|  | 	data.ExpireAt = time.Now().AddDate(0, 0, 30) | ||||||
|  | 	data.PayTime = &now | ||||||
|  | 	data.TotalChars = int(req.TotalChars.Mul(decimal.NewFromInt(10000)).IntPart()) | ||||||
|  |  | ||||||
|  | 	qmgr := quota_manager.NewQuotaManager(redishelper.DefaultRedis.GetClient()) | ||||||
|  |  | ||||||
|  | 	// 事务处理 | ||||||
|  | 	err := e.Orm.Transaction(func(tx *gorm.DB) error { | ||||||
|  | 		//写入充值记录 | ||||||
|  | 		if err1 := e.Orm.Create(&data).Error; err1 != nil { | ||||||
|  | 			return err1 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		//写入可用字符 | ||||||
|  | 		if _, err1 := qmgr.Deduct(ctx, member.ApiKey, platform.Code, int64(data.TotalChars)); err1 != nil { | ||||||
|  | 			return err1 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
| // 新增充值校验 | // 新增充值校验 | ||||||
| func (e *TmRechargeLog) CreateOrderJudge(req *dto.TmRechargeCreateOrderReq) (models.TmMember, *models.TmPlatform, models.TmMemberPlatform, int) { | func (e *TmRechargeLog) CreateOrderJudge(memberId int, platformId int) (models.TmMember, *models.TmPlatform, models.TmMemberPlatform, int) { | ||||||
| 	memberService := TmMember{Service: e.Service} | 	memberService := TmMember{Service: e.Service} | ||||||
| 	member := models.TmMember{} | 	member := models.TmMember{} | ||||||
| 	if err := memberService.GetById(req.MemberId, &member); err != nil { | 	if err := memberService.GetById(memberId, &member); err != nil { | ||||||
| 		return models.TmMember{}, nil, models.TmMemberPlatform{}, statuscode.NotFindMember | 		return models.TmMember{}, nil, models.TmMemberPlatform{}, statuscode.NotFindMember | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -125,7 +187,7 @@ func (e *TmRechargeLog) CreateOrderJudge(req *dto.TmRechargeCreateOrderReq) (mod | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	platformService := TmPlatform{Service: e.Service} | 	platformService := TmPlatform{Service: e.Service} | ||||||
| 	platform, err := platformService.GetById(req.PlatformId) | 	platform, err := platformService.GetById(platformId) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		e.Log.Errorf("获取平台信息失败:%s \r\n", err.Error()) | 		e.Log.Errorf("获取平台信息失败:%s \r\n", err.Error()) | ||||||
| @ -136,10 +198,15 @@ func (e *TmRechargeLog) CreateOrderJudge(req *dto.TmRechargeCreateOrderReq) (mod | |||||||
| 		return models.TmMember{}, nil, models.TmMemberPlatform{}, statuscode.PlatformNotSupport | 		return models.TmMember{}, nil, models.TmMemberPlatform{}, statuscode.PlatformNotSupport | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if platform.BlockChain == "" || platform.ReceiveAddress == "" { | ||||||
|  | 		e.Log.Error("翻译平台未配置收款信息") | ||||||
|  | 		return models.TmMember{}, nil, models.TmMemberPlatform{}, statuscode.PlatformNotSupport | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	memberPlatformService := TmMemberPlatform{Service: e.Service} | 	memberPlatformService := TmMemberPlatform{Service: e.Service} | ||||||
| 	memberPlatform, err := memberPlatformService.GetOrInsert(&dto.TmRechargeLogInsertOrUpdateReq{ | 	memberPlatform, err := memberPlatformService.GetOrInsert(&dto.TmRechargeLogInsertOrUpdateReq{ | ||||||
| 		MemberId:    req.MemberId, | 		MemberId:    memberId, | ||||||
| 		PlatformId:  req.PlatformId, | 		PlatformId:  platformId, | ||||||
| 		PlatformKey: platform.Code, | 		PlatformKey: platform.Code, | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| @ -155,7 +222,7 @@ func (e *TmRechargeLog) CreateOrderJudge(req *dto.TmRechargeCreateOrderReq) (mod | |||||||
|  |  | ||||||
| // 用户自己发起充值 | // 用户自己发起充值 | ||||||
| // return code | // return code | ||||||
| func (e TmRechargeLog) CreateOrder(req *dto.CustomCreateOrderReq, apiKey string) int { | func (e TmRechargeLog) CreateOrder(req *dto.CustomCreateOrderReq, resp *dto.CustomCreateOrderResp, apiKey string) int { | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
| 	var amountStr string | 	var amountStr string | ||||||
| 	// var errReturn bool | 	// var errReturn bool | ||||||
| @ -202,16 +269,22 @@ func (e TmRechargeLog) CreateOrder(req *dto.CustomCreateOrderReq, apiKey string) | |||||||
| 		return statuscode.ServerError | 		return statuscode.ServerError | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	_, platform, _, code := e.CreateOrderJudge(member.Id, req.PlatformId) | ||||||
|  |  | ||||||
|  | 	if code != statuscode.Success { | ||||||
|  | 		return code | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	createReq := dto.TmRechargeCreateOrderReq{} | 	createReq := dto.TmRechargeCreateOrderReq{} | ||||||
| 	createReq.MemberId = member.Id | 	createReq.MemberId = member.Id | ||||||
| 	createReq.Amount = req.Amount | 	createReq.Amount = platform.Price.Mul(decimal.NewFromInt(int64(req.Count))) | ||||||
| 	createReq.PlatformId = req.PlatformId | 	createReq.PlatformId = req.PlatformId | ||||||
| 	createReq.ReceiveAddress = config.ConfigValue | 	createReq.ReceiveAddress = platform.ReceiveAddress | ||||||
| 	createReq.ReceiveChannel = "TRX" | 	createReq.ReceiveChannel = platform.BlockChain | ||||||
| 	createReq.Type = 1 | 	createReq.Type = 1 | ||||||
| 	createReq.UserId = req.UserId | 	createReq.UserId = req.UserId | ||||||
|  |  | ||||||
| 	amountStr, err = receiveaddressmanager.AllocatePaymentAmount(ctx, redishelper.DefaultRedis.GetClient(), req.Amount.InexactFloat64(), timeInterval) | 	amountStr, err = receiveaddressmanager.AllocatePaymentAmount(ctx, redishelper.DefaultRedis.GetClient(), createReq.Amount.InexactFloat64(), timeInterval) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		e.Log.Errorf("分配可用金额失败:%v \r\n", err) | 		e.Log.Errorf("分配可用金额失败:%v \r\n", err) | ||||||
| @ -225,12 +298,6 @@ func (e TmRechargeLog) CreateOrder(req *dto.CustomCreateOrderReq, apiKey string) | |||||||
| 		return statuscode.ServerError | 		return statuscode.ServerError | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	_, platform, _, code := e.CreateOrderJudge(&createReq) |  | ||||||
|  |  | ||||||
| 	if code != statuscode.Success { |  | ||||||
| 		return code |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	data := models.TmRechargeLog{} | 	data := models.TmRechargeLog{} | ||||||
| 	data.Amount = createReq.Amount | 	data.Amount = createReq.Amount | ||||||
| 	platformCharacter := int64(platform.Character * 1000000) | 	platformCharacter := int64(platform.Character * 1000000) | ||||||
| @ -240,6 +307,7 @@ func (e TmRechargeLog) CreateOrder(req *dto.CustomCreateOrderReq, apiKey string) | |||||||
| 	data.Type = createReq.Type | 	data.Type = createReq.Type | ||||||
| 	data.Status = 1 | 	data.Status = 1 | ||||||
| 	data.UserId = member.UserId | 	data.UserId = member.UserId | ||||||
|  | 	data.MemberId = member.Id | ||||||
| 	data.PlatformId = platform.Id | 	data.PlatformId = platform.Id | ||||||
| 	data.ReceiveAddress = createReq.ReceiveAddress | 	data.ReceiveAddress = createReq.ReceiveAddress | ||||||
| 	data.ReceiveChannel = createReq.ReceiveChannel | 	data.ReceiveChannel = createReq.ReceiveChannel | ||||||
| @ -266,13 +334,30 @@ func (e TmRechargeLog) CreateOrder(req *dto.CustomCreateOrderReq, apiKey string) | |||||||
| 		return nil | 		return nil | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return statuscode.ServerError | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp.Amount = data.Amount.String() | ||||||
|  | 	resp.ReceiveAddress = data.ReceiveAddress | ||||||
|  | 	resp.OrderNo = data.OrderNo | ||||||
|  | 	resp.BlockChain = strings.ToUpper(data.ReceiveChannel) | ||||||
|  | 	resp.ExpireUnix = data.OrderExpireTime.Unix() | ||||||
|  |  | ||||||
| 	return statuscode.Success | 	return statuscode.Success | ||||||
| } | } | ||||||
|  |  | ||||||
| // 充值回调 | // 充值回调 | ||||||
| func (e *TmRechargeLog) PayCallBack(logs *[]dto.TmRechargeCallbackReq) error { | func (e *TmRechargeLog) PayCallBack(logs *[]dto.TmRechargeCallbackReq) error { | ||||||
|  | 	now := time.Now() | ||||||
|  | 	qmgr := quota_manager.NewQuotaManager(redishelper.DefaultRedis.GetClient()) | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 	memberService := TmMember{Service: e.Service} | ||||||
|  | 	platformService := TmPlatform{Service: e.Service} | ||||||
|  |  | ||||||
| 	for _, log := range *logs { | 	for _, log := range *logs { | ||||||
| 		key := fmt.Sprintf(rediskey.TM_RECHARGE_PRE_ORDER, log.PayableAmount.String()) | 		amountStr := log.PayableAmount.StringFixed(4) | ||||||
|  | 		key := fmt.Sprintf(rediskey.TM_RECHARGE_PRE_ORDER, amountStr) | ||||||
| 		orderNo, err := redishelper.DefaultRedis.GetString(key) | 		orderNo, err := redishelper.DefaultRedis.GetString(key) | ||||||
|  |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @ -289,7 +374,47 @@ func (e *TmRechargeLog) PayCallBack(logs *[]dto.TmRechargeCallbackReq) error { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if data.Status == 1 { | 		if data.Status == 1 { | ||||||
|  | 			data.TxHash = log.TxHash | ||||||
|  | 			data.Status = 2 | ||||||
|  | 			data.PayTime = &now | ||||||
|  | 			data.ExpireAt = time.Now().AddDate(0, 0, 30) | ||||||
|  | 			member := models.TmMember{} | ||||||
|  | 			err := memberService.GetById(data.MemberId, &member) | ||||||
|  |  | ||||||
|  | 			if err != nil { | ||||||
|  | 				e.Log.Errorf("获取用户信息失败 error:%s \r\n", err) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			platform, err := platformService.GetById(data.PlatformId) | ||||||
|  |  | ||||||
|  | 			if err != nil { | ||||||
|  | 				e.Log.Errorf("获取平台信息失败 error:%s \r\n", err) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			rechargeData := quota_manager.QuotaRecharge{ | ||||||
|  | 				QuotaID:  data.OrderNo, | ||||||
|  | 				Amount:   int64(data.TotalChars), | ||||||
|  | 				ExpireAt: data.ExpireAt.Unix(), | ||||||
|  | 			} | ||||||
|  | 			err = e.Orm.Transaction(func(tx *gorm.DB) error { | ||||||
|  | 				if err1 := e.Orm.Model(&models.TmRechargeLog{}).Where("id =?", data.Id).Updates(map[string]interface{}{"tx_hash": log.TxHash, "status": 2, "pay_time": now}).Error; err1 != nil { | ||||||
|  | 					e.Log.Errorf("发起充值记录失败 error:%s \r\n", err1) | ||||||
|  | 					return err1 | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				//写入可用字符 | ||||||
|  | 				if err1 := qmgr.AddQuota(ctx, member.ApiKey, platform.Code, rechargeData); err1 != nil { | ||||||
|  | 					return err1 | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				return nil | ||||||
|  | 			}) | ||||||
|  |  | ||||||
|  | 			if err != nil { | ||||||
|  | 				e.Log.Errorf("写入可用字符失败 error:%s \r\n", err) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			e.Log.Errorf("订单状态异常:%s 状态:%d\r\n", orderNo, data.Status) | 			e.Log.Errorf("订单状态异常:%s 状态:%d\r\n", orderNo, data.Status) | ||||||
| 		} | 		} | ||||||
| @ -334,3 +459,28 @@ func (e *TmRechargeLog) GetRemainByOrderNo(orderNo string) (int, error) { | |||||||
|  |  | ||||||
| 	return strconv.Atoi(val) | 	return strconv.Atoi(val) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (e *TmRechargeLog) GetPlatforms() ([]models.TmPlatform, error) { | ||||||
|  | 	key1 := fmt.Sprintf(rediskey.TM_PLATFORM_KEY, "*") | ||||||
|  | 	scankeys, err := redishelper.DefaultRedis.ScanKeys(key1) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	platforms := make([]models.TmPlatform, 0) | ||||||
|  | 	for _, key := range scankeys { | ||||||
|  | 		val, err := redishelper.DefaultRedis.GetString(key) | ||||||
|  | 		if err != nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		var platform models.TmPlatform | ||||||
|  | 		sonic.Unmarshal([]byte(val), &platform) | ||||||
|  |  | ||||||
|  | 		if platform.Id > 0 { | ||||||
|  | 			platforms = append(platforms, platform) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return platforms, nil | ||||||
|  | } | ||||||
|  | |||||||
| @ -37,6 +37,9 @@ type TranslatorToolService struct { | |||||||
| 	service.Service | 	service.Service | ||||||
| } | } | ||||||
|  |  | ||||||
|  | var UnSportPlatformErr = errors.New("平台不支持") | ||||||
|  | var TransUnMarshalConfigErr = errors.New("翻译服务商配置解析失败") | ||||||
|  |  | ||||||
| // NewTranslatorService 创建翻译服务实例 | // NewTranslatorService 创建翻译服务实例 | ||||||
| func NewTranslatorService(cfg *TranslatorServiceConfig) (*TranslatorService, error) { | func NewTranslatorService(cfg *TranslatorServiceConfig) (*TranslatorService, error) { | ||||||
| 	svc := &TranslatorService{ | 	svc := &TranslatorService{ | ||||||
| @ -50,30 +53,43 @@ func NewTranslatorService(cfg *TranslatorServiceConfig) (*TranslatorService, err | |||||||
| 		var newAdapter Translator | 		var newAdapter Translator | ||||||
| 		switch providerName { | 		switch providerName { | ||||||
| 		case "google": | 		case "google": | ||||||
| 			googleCfgBytes, _ := json.Marshal(providerCfg) // 假设配置是map[string]interface{},需要转换 | 			googleCfgBytes, _ := json.Marshal(providerCfg) | ||||||
| 			var googleCfg GoogleTranslatorConfig | 			var googleCfg GoogleTranslatorConfig | ||||||
| 			if err := json.Unmarshal(googleCfgBytes, &googleCfg); err != nil { | 			if err := json.Unmarshal(googleCfgBytes, &googleCfg); err != nil { | ||||||
| 				return nil, errors.Wrapf(err, "failed to unmarshal config for GoogleTranslate") | 				logger.Error("failed to unmarshal config for GoogleTranslate") | ||||||
|  | 				return nil, TransUnMarshalConfigErr | ||||||
| 			} | 			} | ||||||
| 			newAdapter = NewGoogleTranslator(&googleCfg) | 			newAdapter = NewGoogleTranslator(&googleCfg) | ||||||
| 		// case "BaiduTranslate": |  | ||||||
| 		// 	// newAdapter = adapter.NewBaiduTranslator(...) |  | ||||||
| 		case "deepseek": | 		case "deepseek": | ||||||
| 			deepseekCfgBytes, _ := json.Marshal(providerCfg) // 假设配置是map[string]interface{},需要转换 | 			deepseekCfgBytes, _ := json.Marshal(providerCfg) | ||||||
| 			var deepseekCfg DeepseekTranslatorConfig | 			var deepseekCfg DeepseekTranslatorConfig | ||||||
| 			if err := json.Unmarshal(deepseekCfgBytes, &deepseekCfg); err != nil { | 			if err := json.Unmarshal(deepseekCfgBytes, &deepseekCfg); err != nil { | ||||||
| 				return nil, errors.Wrapf(err, "failed to unmarshal config for DeepseekTranslator") | 				logger.Error(err, "failed to unmarshal config for DeepseekTranslator") | ||||||
|  |  | ||||||
|  | 				return nil, TransUnMarshalConfigErr | ||||||
| 			} | 			} | ||||||
| 			newAdapter = NewDeepseekTranslator(&deepseekCfg) | 			newAdapter = NewDeepseekTranslator(&deepseekCfg) | ||||||
| 		case "deepl", "deepl_free": | 		case "deepl", "deepl_free": | ||||||
| 			deeplCfgBytes, _ := json.Marshal(providerCfg) // 假设配置是map[string]interface{},需要转换 | 			deeplCfgBytes, _ := json.Marshal(providerCfg) | ||||||
| 			var deeplCfg DeeplTranslatorConfig | 			var deeplCfg DeeplTranslatorConfig | ||||||
| 			if err := json.Unmarshal(deeplCfgBytes, &deeplCfg); err != nil { | 			if err := json.Unmarshal(deeplCfgBytes, &deeplCfg); err != nil { | ||||||
| 				return nil, errors.Wrapf(err, "failed to unmarshal config for DeepLTranslator") | 				logger.Error("failed to unmarshal config for DeepLTranslator") | ||||||
|  | 				return nil, TransUnMarshalConfigErr | ||||||
| 			} | 			} | ||||||
| 			newAdapter = NewDeeplTranslator(&deeplCfg) | 			newAdapter = NewDeeplTranslator(&deeplCfg) | ||||||
|  |  | ||||||
|  | 		//翻译之家 | ||||||
|  | 		case "deepl_trans", "google_trans", "baidu_trans", "youdao_trans", "bing_trans", "huoshan_trans", "chatgpt-4o_trans", "yandex_trans": | ||||||
|  | 			transCfgBytes, _ := json.Marshal(providerCfg) | ||||||
|  | 			var transCfg TransTranslatorConfig | ||||||
|  | 			if err := json.Unmarshal(transCfgBytes, &transCfg); err != nil { | ||||||
|  | 				logger.Error("failed to unmarshal config for Translator") | ||||||
|  | 				return nil, TransUnMarshalConfigErr | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			newAdapter = NewTransTranslator(&transCfg) | ||||||
| 		default: | 		default: | ||||||
| 			return nil, errors.Errorf("unsupported translation provider: %s", providerName) | 			return nil, UnSportPlatformErr | ||||||
| 		} | 		} | ||||||
| 		svc.RegisterProvider(providerName, newAdapter) | 		svc.RegisterProvider(providerName, newAdapter) | ||||||
| 	} | 	} | ||||||
| @ -149,7 +165,7 @@ func (s *TranslatorService) TranslateJudge(req *dto.TranslateReq, apiKey string) | |||||||
| 		platformConfigInterface := Translator.config.ProviderConfigs[req.Platform] | 		platformConfigInterface := Translator.config.ProviderConfigs[req.Platform] | ||||||
|  |  | ||||||
| 		// 尝试将 interface{} 断言为 map[string]interface{} | 		// 尝试将 interface{} 断言为 map[string]interface{} | ||||||
| 		if mapData, ok := platformConfigInterface.(map[string]interface{}); !ok { | 		if mapData, ok := platformConfigInterface.(map[string]interface{}); ok { | ||||||
| 			tmPlatformAccount.DecrRemainBy(req.Platform, mapData["apiKey"].(string), count) | 			tmPlatformAccount.DecrRemainBy(req.Platform, mapData["apiKey"].(string), count) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @ -157,6 +173,7 @@ func (s *TranslatorService) TranslateJudge(req *dto.TranslateReq, apiKey string) | |||||||
| 		redishelper.DefaultRedis.IncrBy(fmt.Sprintf(rediskey.TM_MEMBER_DAILY_COUNT, date, apiKey, req.Platform), int64(count)) | 		redishelper.DefaultRedis.IncrBy(fmt.Sprintf(rediskey.TM_MEMBER_DAILY_COUNT, date, apiKey, req.Platform), int64(count)) | ||||||
| 		//每日统计保留三天 | 		//每日统计保留三天 | ||||||
| 		redishelper.DefaultRedis.Expire(fmt.Sprintf(rediskey.TM_MEMBER_DAILY_COUNT, date, apiKey, req.Platform), 3*24*time.Hour) | 		redishelper.DefaultRedis.Expire(fmt.Sprintf(rediskey.TM_MEMBER_DAILY_COUNT, date, apiKey, req.Platform), 3*24*time.Hour) | ||||||
|  |  | ||||||
| 	} else { | 	} else { | ||||||
| 		tmMemberService.RefundQuote(ctx, apiKey, req.Platform, &decyDatas) | 		tmMemberService.RefundQuote(ctx, apiKey, req.Platform, &decyDatas) | ||||||
|  |  | ||||||
| @ -200,22 +217,20 @@ func (s *TranslatorService) GetTranslator(platform string) (translator *Translat | |||||||
| 		ProviderConfigs: configs, | 		ProviderConfigs: configs, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	switch platform { | 	translator, err = NewTranslatorService(&cfg) | ||||||
| 	case "deepl", "deepl_free", "deepseek": |  | ||||||
| 		translator, err = NewTranslatorService(&cfg) |  | ||||||
|  |  | ||||||
| 		if err != nil { | 	if err != nil { | ||||||
| 			s.Log.Errorf("failed to create translator service: %s", err) | 		switch err { | ||||||
|  | 		case UnSportPlatformErr: | ||||||
|  | 			code = statuscode.PlatformNotSupport | ||||||
|  | 		default: | ||||||
| 			code = statuscode.ServerError | 			code = statuscode.ServerError | ||||||
| 			return |  | ||||||
| 		} | 		} | ||||||
|  | 	} else { | ||||||
| 		code = statuscode.Success | 		code = statuscode.Success | ||||||
| 		return |  | ||||||
| 	default: |  | ||||||
| 		code = statuscode.PlatformNotSupport |  | ||||||
| 		return |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| // 从 Redis List 顺序取出一个账号,使用后放回队尾,模拟轮询 | // 从 Redis List 顺序取出一个账号,使用后放回队尾,模拟轮询 | ||||||
|  | |||||||
							
								
								
									
										108
									
								
								app/admin/service/translator_trans.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								app/admin/service/translator_trans.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"go-admin/app/admin/service/dto" | ||||||
|  | 	"go-admin/utils/httphelper" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // 翻译之家 | ||||||
|  | type TransTranslator struct { | ||||||
|  | 	config *TransTranslatorConfig | ||||||
|  | 	client *httphelper.HTTPClient | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type TransTranslatorConfig struct { | ||||||
|  | 	ApiKey    string `json:"apiKey"` | ||||||
|  | 	ApiSecret string `json:"apiSecret"` | ||||||
|  | 	Endpoint  string `json:"endpoint"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type TransTranslatorResponse[T any] struct { | ||||||
|  | 	Code    int    `json:"code"` | ||||||
|  | 	Message string `json:"message"` | ||||||
|  | 	Data    T      `json:"data"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 剩余字符数 | ||||||
|  | type TransTranslatorResponseNumData struct { | ||||||
|  | 	UseNum int `json:"use_num" comment:"总字符"` | ||||||
|  | 	IsUsed int `json:"is_used" comment:"已用字符"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 翻译内容 | ||||||
|  | type TransTranslatorResponseData struct { | ||||||
|  | 	Text string `json:"text"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 翻译之家 | ||||||
|  | func NewTransTranslator(config *TransTranslatorConfig) *TransTranslator { | ||||||
|  | 	defaultHeaders := map[string]string{ | ||||||
|  | 		"Content-Type": "application/json", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	httpClient := httphelper.NewHTTPClient( | ||||||
|  | 		15*time.Second, | ||||||
|  | 		config.Endpoint, | ||||||
|  | 		defaultHeaders, | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	return &TransTranslator{ | ||||||
|  | 		config: config, | ||||||
|  | 		client: httpClient, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 翻译 | ||||||
|  | func (e *TransTranslator) Translate(text string, sourceLang, targetLang string) (*dto.TranslateResult, error) { | ||||||
|  | 	result := dto.TranslateResult{} | ||||||
|  | 	responseData := TransTranslatorResponse[TransTranslatorResponseData]{} | ||||||
|  | 	route := fmt.Sprintf("/api/index/translate?token=%s", e.config.ApiKey) | ||||||
|  | 	params := map[string]string{ | ||||||
|  | 		"keywords":       text, | ||||||
|  | 		"sourceLanguage": sourceLang, | ||||||
|  | 		"targetLanguage": targetLang, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Deepl API 翻译通常是 POST 请求到 /v2/translate | ||||||
|  | 	err := e.client.Post(route, params, nil, &responseData) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return &result, fmt.Errorf("翻译请求出错: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if responseData.Code != 1 { | ||||||
|  | 		return &result, fmt.Errorf("翻译失败,错误代码: %d msg: %s", responseData.Code, responseData.Message) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if responseData.Data.Text != "" { | ||||||
|  | 		result.TranslatedText = responseData.Data.Text | ||||||
|  | 	} else { | ||||||
|  | 		return &result, fmt.Errorf("翻译失败,返回数据为空") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &result, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *TransTranslator) GetRemainCount() (int, error) { | ||||||
|  | 	responseData := TransTranslatorResponse[TransTranslatorResponseNumData]{} | ||||||
|  | 	route := fmt.Sprintf("/api/index/getUserNums?token=%s", e.config.ApiKey) | ||||||
|  |  | ||||||
|  | 	// Deepl API 翻译通常是 POST 请求到 /v2/translate | ||||||
|  | 	err := e.client.Get(route, nil, &responseData) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, fmt.Errorf("翻译请求出错: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if responseData.Code != 1 { | ||||||
|  | 		return 0, fmt.Errorf("翻译失败,错误代码: %d msg: %s", responseData.Code, responseData.Message) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	result := responseData.Data.UseNum - responseData.Data.IsUsed | ||||||
|  | 	return result, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取服务商 | ||||||
|  | func (t *TransTranslator) GetPlatform() string { | ||||||
|  | 	return "trans" | ||||||
|  | } | ||||||
| @ -10,9 +10,11 @@ import ( | |||||||
| // 字典 key 可以配置到 自动任务 调用目标 中; | // 字典 key 可以配置到 自动任务 调用目标 中; | ||||||
| func InitJob() { | func InitJob() { | ||||||
| 	jobList = map[string]JobExec{ | 	jobList = map[string]JobExec{ | ||||||
| 		"ExamplesOne":   ExamplesOne{}, | 		"ExamplesOne":          ExamplesOne{}, | ||||||
| 		"DailyJob":      DailyJob{}, | 		"DailyJob":             DailyJob{}, | ||||||
| 		"RemainCharJob": RemainCharJob{}, | 		"RemainCharJob":        RemainCharJob{}, | ||||||
|  | 		"CleanExpiredOrderJob": CleanExpiredOrderJob{}, | ||||||
|  | 		"TrxPaymentJob":        TrxPaymentJob{}, | ||||||
| 		// ... | 		// ... | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -12,6 +12,18 @@ type DailyJob struct{} | |||||||
|  |  | ||||||
| type RemainCharJob struct{} | type RemainCharJob struct{} | ||||||
|  |  | ||||||
|  | type CleanExpiredOrderJob struct{} | ||||||
|  |  | ||||||
|  | // 清理过期订单 | ||||||
|  | func (t CleanExpiredOrderJob) Exec(arg interface{}) error { | ||||||
|  | 	// expireTime := time.Now().Add(5 * time.Minute) | ||||||
|  | 	rechargeLogService := service.TmRechargeLog{} | ||||||
|  | 	rechargeLogService.Orm = GetDb() | ||||||
|  | 	rechargeLogService.Log = logger.NewHelper(logger.DefaultLogger) | ||||||
|  |  | ||||||
|  | 	return rechargeLogService.CleanExpiredOrder() | ||||||
|  | } | ||||||
|  |  | ||||||
| // 剩余字符统计 | // 剩余字符统计 | ||||||
| func (t RemainCharJob) Exec(arg interface{}) error { | func (t RemainCharJob) Exec(arg interface{}) error { | ||||||
| 	memberService := service.TmMember{} | 	memberService := service.TmMember{} | ||||||
|  | |||||||
| @ -36,3 +36,17 @@ func TestRemainTranslate(t *testing.T) { | |||||||
| 		t.Error(err) | 		t.Error(err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestClean(t *testing.T) { | ||||||
|  | 	dsn := "root:123456@tcp(127.0.0.1:3306)/aggregate_translate?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms" | ||||||
|  | 	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) | ||||||
|  | 	sdk.Runtime.SetDb("default", db) | ||||||
|  |  | ||||||
|  | 	redishelper.InitDefaultRedis("127.0.0.1:6379", "", 1) | ||||||
|  | 	redishelper.InitLockRedisConn("127.0.0.1:6379", "", "1") | ||||||
|  |  | ||||||
|  | 	job := CleanExpiredOrderJob{} | ||||||
|  | 	if err := job.Exec(nil); err != nil { | ||||||
|  | 		t.Error(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ import ( | |||||||
| 	"go-admin/utils/utility" | 	"go-admin/utils/utility" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/go-admin-team/go-admin-core/logger" | 	"github.com/go-admin-team/go-admin-core/logger" | ||||||
| @ -23,7 +24,7 @@ const ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| // trx 链上支付定时查询 | // trx 链上支付定时查询 | ||||||
| func (j *TrxPaymentJob) Exec(args interface{}) error { | func (j TrxPaymentJob) Exec(arg interface{}) error { | ||||||
| 	configService := service.SysConfig{} | 	configService := service.SysConfig{} | ||||||
| 	configService.Orm = GetDb() | 	configService.Orm = GetDb() | ||||||
| 	req := dto.SysConfigByKeyReq{} | 	req := dto.SysConfigByKeyReq{} | ||||||
| @ -37,39 +38,58 @@ func (j *TrxPaymentJob) Exec(args interface{}) error { | |||||||
|  |  | ||||||
| 	rechargeService := service.TmRechargeLog{} | 	rechargeService := service.TmRechargeLog{} | ||||||
| 	rechargeService.Orm = GetDb() | 	rechargeService.Orm = GetDb() | ||||||
|  | 	platforms, err := rechargeService.GetPlatforms() | ||||||
|  | 	toAddresss := []string{} | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		logger.Error("查询平台失败", err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, platform := range platforms { | ||||||
|  | 		if strings.ToLower(platform.BlockChain) == "trx" && !utility.ContainsString(toAddresss, platform.ReceiveAddress) { | ||||||
|  | 			toAddresss = append(toAddresss, platform.ReceiveAddress) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	startTime := time.Now().UnixMilli() | 	startTime := time.Now().UnixMilli() | ||||||
| 	endTime := time.Now().Add(-1 * time.Hour).UnixMilli() | 	endTime := time.Now().Add(-1 * time.Hour).UnixMilli() | ||||||
| 	transfers, err := GetTRC20Transfers(UsdtContractAddress, configData.ConfigValue, endTime, startTime) |  | ||||||
| 	if err != nil { |  | ||||||
| 		logger.Error("查询失败", err) |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	logs := make([]dto.TmRechargeCallbackReq, 0) |  | ||||||
| 	item := dto.TmRechargeCallbackReq{} |  | ||||||
|  |  | ||||||
| 	for _, transfer := range transfers { |  | ||||||
| 		if transfer.TransactionID == "" || transfer.ToAddress != configData.ConfigValue { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		//实际金额 |  | ||||||
| 		payableAmount := utility.StringToDecimal(transfer.Value).Div(decimal.NewFromInt(10).Pow(decimal.NewFromInt(int64(transfer.TokenInfo.Decimals)))).Truncate(6) |  | ||||||
| 		item.TxHash = transfer.TransactionID |  | ||||||
| 		item.PayableAmount = payableAmount |  | ||||||
| 		item.FromAddress = transfer.FromAddress |  | ||||||
|  |  | ||||||
| 		logs = append(logs, item) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(logs) > 0 { |  | ||||||
| 		err := rechargeService.PayCallBack(&logs) |  | ||||||
|  |  | ||||||
|  | 	for _, toAddress := range toAddresss { | ||||||
|  | 		transfers, err := GetTRC20Transfers(UsdtContractAddress, toAddress, endTime, startTime) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			logger.Error("执行完毕,err:") | 			logger.Error("查询失败", err) | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		logs := make([]dto.TmRechargeCallbackReq, 0) | ||||||
|  | 		item := dto.TmRechargeCallbackReq{} | ||||||
|  |  | ||||||
|  | 		for _, transfer := range transfers { | ||||||
|  | 			if transfer.TransactionID == "" || transfer.ToAddress != toAddress { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			//实际金额 | ||||||
|  | 			payableAmount := utility.StringToDecimal(transfer.Value).Div(decimal.NewFromInt(10).Pow(decimal.NewFromInt(int64(transfer.TokenInfo.Decimals)))).Truncate(6) | ||||||
|  | 			item.TxHash = transfer.TransactionID | ||||||
|  | 			item.PayableAmount = payableAmount | ||||||
|  | 			item.FromAddress = transfer.FromAddress | ||||||
|  | 			item.ToAddress = transfer.ToAddress | ||||||
|  |  | ||||||
|  | 			if utility.ContainsString(toAddresss, item.ToAddress) { | ||||||
|  | 				logs = append(logs, item) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if len(logs) > 0 { | ||||||
|  | 			err := rechargeService.PayCallBack(&logs) | ||||||
|  |  | ||||||
|  | 			if err != nil { | ||||||
|  | 				logger.Error("执行完毕,err:", err.Error()) | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								app/jobs/trxpayment_job_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/jobs/trxpayment_job_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | package jobs | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"go-admin/config" | ||||||
|  | 	"go-admin/utils/redishelper" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/go-admin-team/go-admin-core/sdk" | ||||||
|  | 	"gorm.io/driver/mysql" | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestTrxPaymentJob(t *testing.T) { | ||||||
|  | 	dsn := "root:123456@tcp(127.0.0.1:3306)/aggregate_translate?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms" | ||||||
|  | 	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) | ||||||
|  | 	sdk.Runtime.SetDb("default", db) | ||||||
|  | 	config.ExtConfig.TrxGridUrl = "https://api.trongrid.io" | ||||||
|  | 	redishelper.InitDefaultRedis("127.0.0.1:6379", "", 1) | ||||||
|  | 	redishelper.InitLockRedisConn("127.0.0.1:6379", "", "1") | ||||||
|  |  | ||||||
|  | 	trxPaymentJob := TrxPaymentJob{} | ||||||
|  |  | ||||||
|  | 	err := trxPaymentJob.Exec(nil) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -23,7 +23,7 @@ var ErrorMessage = map[int]string{ | |||||||
| 	NotFindMember:                       "not find member", | 	NotFindMember:                       "not find member", | ||||||
| 	NotFindApiKey:                       "not find api key", | 	NotFindApiKey:                       "not find api key", | ||||||
| 	MemberPlatformNotSupport:            "member platform not support", | 	MemberPlatformNotSupport:            "member platform not support", | ||||||
| 	RechargeAmountMustBeGreaterThanZero: "recharge amount must be greater than zero", | 	RechargeNumberMustBeGreaterThanZero: "The recharge quantity must be greater than 0", | ||||||
| } | } | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @ -47,6 +47,6 @@ const ( | |||||||
| 	MemberPlatformNotSupport = 30003 //用户平台不支持 | 	MemberPlatformNotSupport = 30003 //用户平台不支持 | ||||||
|  |  | ||||||
| 	//================ 充值相关 =============== | 	//================ 充值相关 =============== | ||||||
| 	RechargeAmountMustBeGreaterThanZero = 40001 //充值金额必须大于0 | 	RechargeNumberMustBeGreaterThanZero = 40001 //充值数量必须大于0 | ||||||
|  |  | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ var ExtConfig Extend | |||||||
| type Extend struct { | type Extend struct { | ||||||
| 	AMap       AMap // 这里配置对应配置文件的结构即可 | 	AMap       AMap // 这里配置对应配置文件的结构即可 | ||||||
| 	Mq         MqConfig | 	Mq         MqConfig | ||||||
| 	TrxGridUrl string `yaml:"trx_grid_url"` | 	TrxGridUrl string `yaml:"trxGridUrl"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type AMap struct { | type AMap struct { | ||||||
|  | |||||||
| @ -54,7 +54,7 @@ settings: | |||||||
|       password: '123456' |       password: '123456' | ||||||
|       pass: "123456" |       pass: "123456" | ||||||
|     #trx api |     #trx api | ||||||
|     trx_grid_url: "https://api.trongrid.io" |     trxGridUrl: "https://api.trongrid.io" | ||||||
|   cache: |   cache: | ||||||
|     redis: |     redis: | ||||||
|       addr: 127.0.0.1:6379 |       addr: 127.0.0.1:6379 | ||||||
|  | |||||||
							
								
								
									
										35
									
								
								utils/chainhelper/chainhelper.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								utils/chainhelper/chainhelper.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | package chainhelper | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"regexp" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // 比较钱包地址格式 | ||||||
|  | func JudgeChainAddress(chain, walletAddress string) error { | ||||||
|  | 	switch chain { | ||||||
|  | 	case "trx": | ||||||
|  | 		return validateTronAddress(walletAddress) | ||||||
|  | 	default: | ||||||
|  | 		return errors.New("invalid chain") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func validateTronAddress(address string) error { | ||||||
|  | 	// TRON 地址通常以 'T' 开头,并且是 34 个字符的 Base58Check 编码 | ||||||
|  | 	// 这是一个简化的检查,最佳实践是使用专门的 Tron 地址验证库来包含校验和验证 | ||||||
|  | 	if len(address) != 34 { | ||||||
|  | 		return errors.New("Tron 钱包地址长度不正确") | ||||||
|  | 	} | ||||||
|  | 	if address[0] != 'T' { | ||||||
|  | 		return errors.New("Tron 钱包地址长度不正确,必须以 'T'开头") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 检查字符集 | ||||||
|  | 	matched, _ := regexp.MatchString("^[T][1-9a-zA-Z]{33}$", address) | ||||||
|  | 	if !matched { | ||||||
|  | 		return errors.New("TRON 钱包地址格式不正确") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @ -27,3 +27,13 @@ func ContainsInt(arr []int, v int) bool { | |||||||
| 	} | 	} | ||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func ContainsString(arr []string, v string) bool { | ||||||
|  | 	for _, a := range arr { | ||||||
|  | 		if a == v { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user