diff --git a/app/admin/apis/tm_recharge_log.go b/app/admin/apis/tm_recharge_log.go index e9df7f1..88f9e99 100644 --- a/app/admin/apis/tm_recharge_log.go +++ b/app/admin/apis/tm_recharge_log.go @@ -47,7 +47,7 @@ func (e TmRechargeLog) GetPage(c *gin.Context) { // 创建充值订单 func (e TmRechargeLog) CreateOrder(c *gin.Context) { - req := dto.TmRechargeCreateOrderReq{} + req := dto.CustomCreateOrderReq{} s := service.TmRechargeLog{} err := e.MakeContext(c). MakeOrm(). @@ -62,6 +62,10 @@ func (e TmRechargeLog) CreateOrder(c *gin.Context) { apiKey := c.GetString("apiKey") + if code := req.Validate(); code != statuscode.Success { + e.Error(code, nil, statuscode.ErrorMessage[code]) + } + code := s.CreateOrder(&req, apiKey) if code != statuscode.Success { e.OK(code, statuscode.ErrorMessage[code]) diff --git a/app/admin/apis/tm_recharge_package.go b/app/admin/apis/tm_recharge_package.go index 2794810..92d8bbe 100644 --- a/app/admin/apis/tm_recharge_package.go +++ b/app/admin/apis/tm_recharge_package.go @@ -1,7 +1,7 @@ package apis import ( - "fmt" + "fmt" "github.com/gin-gonic/gin" "github.com/go-admin-team/go-admin-core/sdk/api" @@ -12,6 +12,7 @@ import ( "go-admin/app/admin/service" "go-admin/app/admin/service/dto" "go-admin/common/actions" + "go-admin/common/statuscode" ) type TmRechargePackage struct { @@ -30,18 +31,18 @@ type TmRechargePackage struct { // @Router /api/v1/tm-recharge-package [get] // @Security Bearer func (e TmRechargePackage) GetPage(c *gin.Context) { - req := dto.TmRechargePackageGetPageReq{} - s := service.TmRechargePackage{} - err := e.MakeContext(c). - MakeOrm(). - Bind(&req). - MakeService(&s.Service). - Errors - if err != nil { - e.Logger.Error(err) - e.Error(500, err, err.Error()) - return - } + req := dto.TmRechargePackageGetPageReq{} + s := service.TmRechargePackage{} + 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.TmRechargePackage, 0) @@ -50,7 +51,7 @@ func (e TmRechargePackage) GetPage(c *gin.Context) { err = s.GetPage(&req, p, &list, &count) if err != nil { e.Error(500, err, fmt.Sprintf("获取tm_recharge_package失败,\r\n失败信息 %s", err.Error())) - return + return } e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") @@ -67,7 +68,7 @@ func (e TmRechargePackage) GetPage(c *gin.Context) { func (e TmRechargePackage) Get(c *gin.Context) { req := dto.TmRechargePackageGetReq{} s := service.TmRechargePackage{} - err := e.MakeContext(c). + err := e.MakeContext(c). MakeOrm(). Bind(&req). MakeService(&s.Service). @@ -83,10 +84,10 @@ func (e TmRechargePackage) Get(c *gin.Context) { err = s.Get(&req, p, &object) if err != nil { e.Error(500, err, fmt.Sprintf("获取tm_recharge_package失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( object, "查询成功") + e.OK(object, "查询成功") } // Insert 创建tm_recharge_package @@ -100,25 +101,25 @@ func (e TmRechargePackage) Get(c *gin.Context) { // @Router /api/v1/tm-recharge-package [post] // @Security Bearer func (e TmRechargePackage) Insert(c *gin.Context) { - req := dto.TmRechargePackageInsertReq{} - s := service.TmRechargePackage{} - err := e.MakeContext(c). - MakeOrm(). - Bind(&req). - MakeService(&s.Service). - Errors - if err != nil { - e.Logger.Error(err) - e.Error(500, err, err.Error()) - return - } + req := dto.TmRechargePackageInsertReq{} + s := service.TmRechargePackage{} + 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("创建tm_recharge_package失败,\r\n失败信息 %s", err.Error())) - return + return } e.OK(req.GetId(), "创建成功") @@ -136,27 +137,27 @@ func (e TmRechargePackage) Insert(c *gin.Context) { // @Router /api/v1/tm-recharge-package/{id} [put] // @Security Bearer func (e TmRechargePackage) Update(c *gin.Context) { - req := dto.TmRechargePackageUpdateReq{} - s := service.TmRechargePackage{} - err := e.MakeContext(c). - MakeOrm(). - Bind(&req). - MakeService(&s.Service). - Errors - if err != nil { - e.Logger.Error(err) - e.Error(500, err, err.Error()) - return - } + req := dto.TmRechargePackageUpdateReq{} + s := service.TmRechargePackage{} + 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("修改tm_recharge_package失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "修改成功") + e.OK(req.GetId(), "修改成功") } // Delete 删除tm_recharge_package @@ -168,18 +169,18 @@ func (e TmRechargePackage) Update(c *gin.Context) { // @Router /api/v1/tm-recharge-package [delete] // @Security Bearer func (e TmRechargePackage) Delete(c *gin.Context) { - s := service.TmRechargePackage{} - req := dto.TmRechargePackageDeleteReq{} - err := e.MakeContext(c). - MakeOrm(). - Bind(&req). - MakeService(&s.Service). - Errors - if err != nil { - e.Logger.Error(err) - e.Error(500, err, err.Error()) - return - } + s := service.TmRechargePackage{} + req := dto.TmRechargePackageDeleteReq{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } // req.SetUpdateBy(user.GetUserId(c)) p := actions.GetPermissionFromContext(c) @@ -187,7 +188,34 @@ func (e TmRechargePackage) Delete(c *gin.Context) { err = s.Remove(&req, p) if err != nil { e.Error(500, err, fmt.Sprintf("删除tm_recharge_package失败,\r\n失败信息 %s", err.Error())) - return + return } - e.OK( req.GetId(), "删除成功") + e.OK(req.GetId(), "删除成功") +} + +// GetFrontList 获取前端列表 +func (e TmRechargePackage) GetFrontList(c *gin.Context) { + req := dto.TmRechargePackageGetListReq{} + s := service.TmRechargePackage{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(statuscode.ServerError, nil, statuscode.ErrorMessage[statuscode.ServerError]) + return + } + + userId := user.GetUserId(c) + list := make([]dto.TmRechargePackageGetListResp, 0) + + code := s.GetFrontList(&req, userId, &list) + if code != statuscode.Success { + e.Error(code, nil, statuscode.ErrorMessage[code]) + return + } + + e.OK(list, "success") } diff --git a/app/admin/apis/translate.go b/app/admin/apis/translate.go index ef55a7c..460e9d6 100644 --- a/app/admin/apis/translate.go +++ b/app/admin/apis/translate.go @@ -16,7 +16,7 @@ type Translate struct { } // 公开翻译接口 -func (e *Translate) Translate(c *gin.Context) { +func (e Translate) Translate(c *gin.Context) { req := dto.TranslateReq{} s := service.TranslatorService{} @@ -50,7 +50,7 @@ func (e *Translate) Translate(c *gin.Context) { } // 获取个人翻译统计 -func (e *Translate) GetTranslateStatistic(c *gin.Context) { +func (e Translate) GetTranslateStatistic(c *gin.Context) { req := dto.TranslateStatisticReq{} s := service.TmMember{} err := e.MakeContext(c). diff --git a/app/admin/models/tm_platform.go b/app/admin/models/tm_platform.go index f4ae86f..b2e749b 100644 --- a/app/admin/models/tm_platform.go +++ b/app/admin/models/tm_platform.go @@ -14,7 +14,7 @@ type TmPlatform struct { ApiBaseUrl string `json:"apiBaseUrl" gorm:"type:varchar(500);comment:平台接口地址"` Description string `json:"description" gorm:"type:varchar(255);comment:描述"` Code string `json:"code" gorm:"type:varchar(20);comment:平台编码(字典 tm_platform)"` - Character string `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:单价"` models.ModelTime models.ControlBy diff --git a/app/admin/models/tm_recharge_log.go b/app/admin/models/tm_recharge_log.go index cf2e072..13270ce 100644 --- a/app/admin/models/tm_recharge_log.go +++ b/app/admin/models/tm_recharge_log.go @@ -10,19 +10,20 @@ import ( type TmRechargeLog struct { models.Model - Type int `json:"type" gorm:"column:type;type:tinyint;not null;comment:类型 1-充值 2-后台充值 3-赠送"` - OrderNo string `json:"orderNo" gorm:"column:order_no;type:varchar(36);not null;comment:订单号"` - UserId int `json:"userId" gorm:"column:user_id;type:bigint;not null;comment:用户id"` - MemberId int `json:"memberId" gorm:"column:member_id;type:bigint;not null;comment:翻译用户id"` - PlatformId int `json:"platformId" gorm:"column:platform_id;type:bigint;not null;comment:平台id"` - Status int `json:"status" gorm:"column:status;type:tinyint;not null;comment:状态 1-待支付 2-已支付 3-已取消 4-申请退款 5-已退款 6-已过期"` - TotalChars int `json:"totalChars" gorm:"column:total_chars;type:bigint;not null;comment:充值字符数"` - TxHash string `json:"txHash" gorm:"column:tx_hash;type:varchar(64);comment:交易hash"` - Amount decimal.Decimal `json:"amount" gorm:"column:amount;type:decimal(10,6);not null;comment:充值金额U"` - ReceiveChannel string `json:"receiveChannel" gorm:"column:receive_channel;type:varchar(100);not null;comment:充值渠道"` - ReceiveAddress string `json:"receiveAddress" gorm:"column:receive_address;type:varchar(100);not null;comment:充值地址"` - PayTime *time.Time `json:"payTime" gorm:"column:pay_time;type:datetime;comment:支付时间"` - ExpireAt time.Time `json:"expireAt" gorm:"column:expire_at;type:datetime;not null;comment:过期时间"` + Type int `json:"type" gorm:"column:type;type:tinyint;not null;comment:类型 1-充值 2-后台充值 3-赠送"` + OrderNo string `json:"orderNo" gorm:"column:order_no;type:varchar(36);not null;comment:订单号"` + UserId int `json:"userId" gorm:"column:user_id;type:bigint;not null;comment:用户id"` + MemberId int `json:"memberId" gorm:"column:member_id;type:bigint;not null;comment:翻译用户id"` + PlatformId int `json:"platformId" gorm:"column:platform_id;type:bigint;not null;comment:平台id"` + Status int `json:"status" gorm:"column:status;type:tinyint;not null;comment:状态 1-待支付 2-已支付 3-已取消 4-申请退款 5-已退款 6-已过期"` + TotalChars int `json:"totalChars" gorm:"column:total_chars;type:bigint;not null;comment:充值字符数"` + TxHash string `json:"txHash" gorm:"column:tx_hash;type:varchar(64);comment:交易hash"` + Amount decimal.Decimal `json:"amount" gorm:"column:amount;type:decimal(10,6);not null;comment:充值金额U"` + ReceiveChannel string `json:"receiveChannel" gorm:"column:receive_channel;type:varchar(100);not null;comment:充值渠道"` + ReceiveAddress string `json:"receiveAddress" gorm:"column:receive_address;type:varchar(100);not null;comment:充值地址"` + PayTime *time.Time `json:"payTime" gorm:"column:pay_time;type:datetime;comment:支付时间"` + ExpireAt time.Time `json:"expireAt" gorm:"column:expire_at;type:datetime;not null;comment:过期时间"` + OrderExpireTime *time.Time `json:"orderExpireTime" gorm:"column:order_expire_time;type:datetime;comment:订单过期时间"` models.ModelTime models.ControlBy } diff --git a/app/admin/service/dto/tm_platform.go b/app/admin/service/dto/tm_platform.go index fb40885..52fc988 100644 --- a/app/admin/service/dto/tm_platform.go +++ b/app/admin/service/dto/tm_platform.go @@ -46,7 +46,7 @@ type TmPlatformInsertReq struct { ApiBaseUrl string `json:"apiBaseUrl" comment:"平台接口地址"` Description string `json:"description" comment:"描述"` Code string `json:"code" comment:"平台编码(字典 tm_platform)"` - Character string `json:"character" comment:"字符数"` + Character int `json:"character" comment:"字符数"` Price decimal.Decimal `json:"price" comment:"单价"` common.ControlBy } @@ -83,7 +83,7 @@ type TmPlatformUpdateReq struct { ApiBaseUrl string `json:"apiBaseUrl" comment:"平台接口地址"` Description string `json:"description" comment:"描述"` Code string `json:"code" comment:"平台编码(字典 tm_platform)"` - Character string `json:"character" comment:"字符数"` + Character int `json:"character" comment:"字符数"` Price decimal.Decimal `json:"price" comment:"单价"` common.ControlBy } diff --git a/app/admin/service/dto/tm_recharge_log.go b/app/admin/service/dto/tm_recharge_log.go index f7b24ac..ba9d0e2 100644 --- a/app/admin/service/dto/tm_recharge_log.go +++ b/app/admin/service/dto/tm_recharge_log.go @@ -5,6 +5,7 @@ import ( "go-admin/app/admin/models" "go-admin/common/dto" common "go-admin/common/models" + "go-admin/common/statuscode" "time" "github.com/shopspring/decimal" @@ -93,7 +94,7 @@ type TmRechargeLogResp struct { CreatedAt string `json:"createdAt"` } -// 用户充值订单创建请求参数 +// 后台-用户充值订单创建请求参数 type TmRechargeCreateOrderReq struct { Type int `json:"type" comment:"充值类型 1-用户充值 2-平台充值"` MemberId int `json:"memberId" comment:"会员id"` @@ -108,6 +109,25 @@ type TmRechargeCreateOrderReq struct { common.ControlBy } +// 用户充值订单创建请求参数 +type CustomCreateOrderReq struct { + UserId int `json:"userId" comment:"用户id"` + Amount decimal.Decimal `json:"amount" comment:"充值金额"` + PlatformId int `json:"platformId" comment:"平台id"` +} + +func (e *CustomCreateOrderReq) Validate() int { + if e.Amount.Cmp(decimal.Zero) <= 0 { + return statuscode.RechargeAmountMustBeGreaterThanZero + } + + if e.PlatformId <= 0 { + return statuscode.PlatformNotSupport + } + + return statuscode.Success +} + func (e *TmRechargeCreateOrderReq) Validate() error { if e.ExpireDays <= 0 { return errors.New("过期天数必须大于0") @@ -163,3 +183,25 @@ type TmRechargeLogFrontResp struct { TotalCharater int `json:"totalCharater"` RemainCharater int `json:"remainCharater"` } + +// TRC20Transfer 结构体用于解析 TRC20 转账记录 +type TRC20Transfer struct { + TransactionID string `json:"transaction_id"` + BlockNumber int64 `json:"block_number"` + Timestamp int64 `json:"block_timestamp"` + FromAddress string `json:"from"` + ToAddress string `json:"to"` + Value string `json:"value"` + TokenInfo struct { + Symbol string `json:"symbol"` + Address string `json:"address"` + Decimals int `json:"decimals"` + } `json:"token_info"` +} + +type TmRechargeCallbackReq struct { + PayableAmount decimal.Decimal `json:"payable_amount"` + FromAddress string `json:"from_address"` + ToAddress string `json:"to_address"` + TxHash string `json:"tx_hash"` +} diff --git a/app/admin/service/dto/tm_recharge_package.go b/app/admin/service/dto/tm_recharge_package.go index eb9e45b..9c87c9c 100644 --- a/app/admin/service/dto/tm_recharge_package.go +++ b/app/admin/service/dto/tm_recharge_package.go @@ -92,3 +92,13 @@ type TmRechargePackageDeleteReq struct { func (s *TmRechargePackageDeleteReq) GetId() interface{} { return s.Ids } + +type TmRechargePackageGetListReq struct { + PlatformId int `form:"platformId" comment:"平台id"` +} + +type TmRechargePackageGetListResp struct { + Id int `json:"id" comment:"主键id"` // 主键id + PlatformId int `json:"platformId" comment:"平台id"` + Amount decimal.Decimal `json:"amount" comment:"套餐金额(U)"` +} diff --git a/app/admin/service/receive_address_manager/receive_address_manager.go b/app/admin/service/receive_address_manager/receive_address_manager.go index 940da55..2aac734 100644 --- a/app/admin/service/receive_address_manager/receive_address_manager.go +++ b/app/admin/service/receive_address_manager/receive_address_manager.go @@ -26,11 +26,13 @@ func LockAmount(ctx context.Context, rdb *redis.Client, amount string, ttl time. } // 金额分配逻辑 -func AllocatePaymentAmount(ctx context.Context, rdb *redis.Client, baseAmount float64) (string, error) { +func AllocatePaymentAmount(ctx context.Context, rdb *redis.Client, baseAmount float64, timeInterval int) (string, error) { + ttl := time.Duration(timeInterval+1) * time.Minute + for i := 0; i < 100; i++ { amount := baseAmount + float64(i)*0.0001 amountStr := fmt.Sprintf("%.4f", amount) - ok, err := LockAmount(ctx, rdb, amountStr, 10*time.Minute) + ok, err := LockAmount(ctx, rdb, amountStr, ttl) if err != nil { return "", err } diff --git a/app/admin/service/tm_recharge_log.go b/app/admin/service/tm_recharge_log.go index 56c7a00..73697d9 100644 --- a/app/admin/service/tm_recharge_log.go +++ b/app/admin/service/tm_recharge_log.go @@ -7,8 +7,10 @@ import ( "go-admin/app/admin/models" "go-admin/app/admin/service/dto" "go-admin/app/admin/service/quota_manager" + receiveaddressmanager "go-admin/app/admin/service/receive_address_manager" "go-admin/common/actions" cDto "go-admin/common/dto" + rediskey "go-admin/common/redis_key" "go-admin/common/statuscode" "go-admin/utils/redishelper" "go-admin/utils/utility" @@ -17,6 +19,8 @@ import ( "time" "github.com/go-admin-team/go-admin-core/sdk/service" + "github.com/go-redis/redis/v8" + "github.com/shopspring/decimal" "gorm.io/gorm" ) @@ -149,28 +153,155 @@ func (e *TmRechargeLog) CreateOrderJudge(req *dto.TmRechargeCreateOrderReq) (mod return member, platform, memberPlatform, statuscode.Success } -// 用户发起充值 +// 用户自己发起充值 // return code -func (e TmRechargeLog) CreateOrder(req *dto.TmRechargeCreateOrderReq, apiKey string) int { - // ctx := context.Background() - // var data models.TmRechargeLog - // member, platform, memberPlatform, code := e.CreateOrderJudge(req) +func (e TmRechargeLog) CreateOrder(req *dto.CustomCreateOrderReq, apiKey string) int { + ctx := context.Background() + var amountStr string + // var errReturn bool - // if code != statuscode.Success { - // return code - // } - // req.Generate(&data) - // now := time.Now() - // data.OrderNo = utility.GenerateTraceID() - // data.Type = 1 - // data.Status = 1 - // data.UserId = member.UserId - // data.ExpireAt = time.Now().AddDate(0, 0, 30) - // data.PaymentTime = &now + //todo 如果没有成功取消金额锁 + // defer func() { + // if amountStr != "" && errReturn { + + // } + // }() + + memberService := TmMember{Service: e.Service} + member := models.TmMember{} + if err1 := memberService.GetByUserId(req.UserId, &member); err1 != nil { + e.Log.Errorf("获取翻译用户失败 :%v", err1) + return statuscode.NotFindMember + } else if member.Id == 0 { + e.Log.Errorf("获取翻译用户失败 用户不存在") + } + + configService := SysConfig{Service: e.Service} + config := dto.GetSysConfigByKEYForServiceResp{} + orderExpireConfig := dto.GetSysConfigByKEYForServiceResp{} + configReq := dto.SysConfigByKeyReq{} + orderExpireConfigReq := dto.SysConfigByKeyReq{} + configReq.ConfigKey = "trx_receive_address" + orderExpireConfigReq.ConfigKey = "order_expire_minute" + + err := configService.GetWithKey(&configReq, &config) + + if err != nil { + e.Log.Errorf("获取配置信息失败:%s \r\n", err.Error()) + return statuscode.ServerError + } + + if err := configService.GetWithKey(&orderExpireConfigReq, &orderExpireConfig); err != nil { + e.Log.Errorf("获取订单过期时间配置失败 %v", err) + return statuscode.ServerError + } + + timeInterval, err := strconv.Atoi(orderExpireConfig.ConfigValue) + if err != nil { + e.Log.Errorf("获取订单过期时间失败 %v", err) + return statuscode.ServerError + } + + createReq := dto.TmRechargeCreateOrderReq{} + createReq.MemberId = member.Id + createReq.Amount = req.Amount + createReq.PlatformId = req.PlatformId + createReq.ReceiveAddress = config.ConfigValue + createReq.ReceiveChannel = "TRX" + createReq.Type = 1 + createReq.UserId = req.UserId + + amountStr, err = receiveaddressmanager.AllocatePaymentAmount(ctx, redishelper.DefaultRedis.GetClient(), req.Amount.InexactFloat64(), timeInterval) + + if err != nil { + e.Log.Errorf("分配可用金额失败:%v \r\n", err) + return statuscode.ServerError + } + + createReq.Amount, err = decimal.NewFromString(amountStr) + + if err != nil { + e.Log.Errorf("转换可用金额失败:%v \r\n", err) + return statuscode.ServerError + } + + _, platform, _, code := e.CreateOrderJudge(&createReq) + + if code != statuscode.Success { + return code + } + + data := models.TmRechargeLog{} + data.Amount = createReq.Amount + platformCharacter := int64(platform.Character * 1000000) + data.TotalChars = int(createReq.Amount.Div(platform.Price).Mul(decimal.NewFromInt(platformCharacter)).IntPart()) + now := time.Now() + data.OrderNo = utility.GenerateTraceID() + data.Type = createReq.Type + data.Status = 1 + data.UserId = member.UserId + data.PlatformId = platform.Id + data.ReceiveAddress = createReq.ReceiveAddress + data.ReceiveChannel = createReq.ReceiveChannel + orderExpireDruation := time.Duration(timeInterval) * time.Minute + orderExpireTime := time.Now().Add(orderExpireDruation) + data.OrderExpireTime = &orderExpireTime + data.ExpireAt = time.Now().AddDate(0, 0, 30) + data.PayTime = &now + + cacheExpireDuration := time.Duration(timeInterval+1) * time.Minute + key := fmt.Sprintf(rediskey.TM_RECHARGE_PRE_ORDER, amountStr) + + err = e.Orm.Transaction(func(tx *gorm.DB) error { + if err1 := e.Orm.Model(&models.TmRechargeLog{}).Save(&data).Error; err1 != nil { + e.Log.Errorf("发起充值记录失败 error:%s \r\n", err1) + return err1 + } + + if err1 := redishelper.DefaultRedis.SetStringExpire(key, data.OrderNo, cacheExpireDuration); err1 != nil { + e.Log.Errorf("设置预充值订单缓存失败 error:%s \r\n") + return err1 + } + + return nil + }) return statuscode.Success } +// 充值回调 +func (e *TmRechargeLog) PayCallBack(logs *[]dto.TmRechargeCallbackReq) error { + for _, log := range *logs { + key := fmt.Sprintf(rediskey.TM_RECHARGE_PRE_ORDER, log.PayableAmount.String()) + orderNo, err := redishelper.DefaultRedis.GetString(key) + + if err != nil { + if err != redis.Nil { + e.Log.Errorf("获取预充值订单失败 error:%s \r\n", err) + } + continue + } + + var data models.TmRechargeLog + if err := e.Orm.Model(&models.TmRechargeLog{}).Where("order_no = ?", orderNo).First(&data).Error; err != nil { + e.Log.Errorf("获取充值记录失败 error:%s \r\n", err) + continue + } + + if data.Status == 1 { + + } else { + e.Log.Errorf("订单状态异常:%s 状态:%d\r\n", orderNo, data.Status) + } + + if err := redishelper.DefaultRedis.DeleteString(key); err != nil { + e.Log.Errorf("删除预充值订单缓存失败 error:%s \r\n", err) + } + } + + return nil +} + // 分页查询 func (e *TmRechargeLog) GetPage(req *dto.TmRechargeLogGetPageReq, p *actions.DataPermission, datas *[]dto.TmRechargeLogResp, count *int64) error { var err error diff --git a/app/admin/service/tm_recharge_package.go b/app/admin/service/tm_recharge_package.go index 11bb446..107364c 100644 --- a/app/admin/service/tm_recharge_package.go +++ b/app/admin/service/tm_recharge_package.go @@ -3,19 +3,44 @@ package service import ( "errors" - "github.com/go-admin-team/go-admin-core/sdk/service" + "github.com/go-admin-team/go-admin-core/sdk/service" "gorm.io/gorm" "go-admin/app/admin/models" "go-admin/app/admin/service/dto" "go-admin/common/actions" cDto "go-admin/common/dto" + "go-admin/common/statuscode" ) type TmRechargePackage struct { service.Service } +// 获取前端查看的金额 +func (e TmRechargePackage) GetFrontList(req *dto.TmRechargePackageGetListReq, userId int, resp *[]dto.TmRechargePackageGetListResp) int { + var datas []models.TmRechargePackage + + err := e.Orm.Model(&models.TmRechargePackage{}). + Where("platform_id =?", req.PlatformId). + Find(&datas).Error + + if err != nil { + e.Log.Errorf("TmRechargePackageService GetFrontList error:%s \r\n", err) + return statuscode.ServerError + } + + for _, data := range datas { + respData := dto.TmRechargePackageGetListResp{ + Id: data.Id, + PlatformId: data.PlatformId, + Amount: data.Amount, + } + *resp = append(*resp, respData) + } + return statuscode.Success +} + // GetPage 获取TmRechargePackage列表 func (e *TmRechargePackage) GetPage(c *dto.TmRechargePackageGetPageReq, p *actions.DataPermission, list *[]models.TmRechargePackage, count *int64) error { var err error @@ -59,9 +84,9 @@ func (e *TmRechargePackage) Get(d *dto.TmRechargePackageGetReq, p *actions.DataP // Insert 创建TmRechargePackage对象 func (e *TmRechargePackage) Insert(c *dto.TmRechargePackageInsertReq) error { - var err error - var data models.TmRechargePackage - c.Generate(&data) + var err error + var data models.TmRechargePackage + c.Generate(&data) err = e.Orm.Create(&data).Error if err != nil { e.Log.Errorf("TmRechargePackageService Insert error:%s \r\n", err) @@ -72,22 +97,22 @@ func (e *TmRechargePackage) Insert(c *dto.TmRechargePackageInsertReq) error { // Update 修改TmRechargePackage对象 func (e *TmRechargePackage) Update(c *dto.TmRechargePackageUpdateReq, p *actions.DataPermission) error { - var err error - var data = models.TmRechargePackage{} - e.Orm.Scopes( - actions.Permission(data.TableName(), p), - ).First(&data, c.GetId()) - c.Generate(&data) + var err error + var data = models.TmRechargePackage{} + 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("TmRechargePackageService Save error:%s \r\n", err) - return err - } - if db.RowsAffected == 0 { - return errors.New("无权更新该数据") - } - return nil + db := e.Orm.Save(&data) + if err = db.Error; err != nil { + e.Log.Errorf("TmRechargePackageService Save error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权更新该数据") + } + return nil } // Remove 删除TmRechargePackage @@ -99,11 +124,11 @@ func (e *TmRechargePackage) Remove(d *dto.TmRechargePackageDeleteReq, p *actions actions.Permission(data.TableName(), p), ).Delete(&data, d.GetId()) if err := db.Error; err != nil { - e.Log.Errorf("Service RemoveTmRechargePackage error:%s \r\n", err) - return err - } - if db.RowsAffected == 0 { - return errors.New("无权删除该数据") - } + e.Log.Errorf("Service RemoveTmRechargePackage error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权删除该数据") + } return nil } diff --git a/app/admin/service/translator_deepl.go b/app/admin/service/translator_deepl.go index b57d4a0..6b624e3 100644 --- a/app/admin/service/translator_deepl.go +++ b/app/admin/service/translator_deepl.go @@ -4,7 +4,6 @@ import ( "fmt" "go-admin/app/admin/service/dto" "go-admin/utils/httphelper" - "strings" "time" ) @@ -63,9 +62,11 @@ func (e *DeeplTranslator) Translate(text string, sourceLang, targetLang string) "Authorization": fmt.Sprintf("DeepL-Auth-Key %s", e.config.ApiKey), "Content-Type": "application/json", } - if sourceLang != "" && strings.ToLower(sourceLang) == "auto" { + + switch sourceLang { + case "", "auto", "AUTO": requestBody["source_lang"] = "" - } else { + default: requestBody["source_lang"] = sourceLang } diff --git a/app/jobs/trxpayment_job.go b/app/jobs/trxpayment_job.go new file mode 100644 index 0000000..8571fd0 --- /dev/null +++ b/app/jobs/trxpayment_job.go @@ -0,0 +1,103 @@ +package jobs + +import ( + "encoding/json" + "fmt" + "go-admin/app/admin/service" + "go-admin/app/admin/service/dto" + "go-admin/config" + "go-admin/utils/utility" + "io/ioutil" + "net/http" + "time" + + "github.com/go-admin-team/go-admin-core/logger" + "github.com/shopspring/decimal" +) + +type TrxPaymentJob struct{} + +const ( + // tronGridURL = "https://api.trongrid.io" // TronGrid API 地址 + UsdtContractAddress = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" //TRX USDT 合约地址 +) + +// trx 链上支付定时查询 +func (j *TrxPaymentJob) Exec(args interface{}) error { + configService := service.SysConfig{} + configService.Orm = GetDb() + req := dto.SysConfigByKeyReq{} + req.ConfigKey = "trx_receive_address" + configData := dto.GetSysConfigByKEYForServiceResp{} + configService.GetWithKey(&req, &configData) + if configData.ConfigValue == "" { + logger.Error("查询地址为空") + return nil + } + + rechargeService := service.TmRechargeLog{} + rechargeService.Orm = GetDb() + startTime := time.Now().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) + + if err != nil { + logger.Error("执行完毕,err:") + } + } + + return nil +} + +// GetTRC20Transfers 获取指定 TRC20 代币的交易记录 +func GetTRC20Transfers(contractAddress, accountAddress string, minTimestamp, maxTimestamp int64) ([]dto.TRC20Transfer, error) { + url := fmt.Sprintf("%s/v1/accounts/%s/transactions/trc20?contract_address=%s", config.ExtConfig.TrxGridUrl, accountAddress, contractAddress) + if minTimestamp > 0 { + url += fmt.Sprintf("&min_timestamp=%d", minTimestamp) + } + if maxTimestamp > 0 { + url += fmt.Sprintf("&max_timestamp=%d", maxTimestamp) + } + resp, err := http.Get(url) + if err != nil { + return nil, fmt.Errorf("failed to send request: %v", err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %v", err) + } + + var result struct { + Data []dto.TRC20Transfer `json:"data"` + } + if err := json.Unmarshal(body, &result); err != nil { + return nil, fmt.Errorf("failed to unmarshal response: %v", err) + } + return result.Data, nil +} diff --git a/common/redis_key/tm_member.go b/common/redis_key/tm_member.go index 24e8e10..e30ba96 100644 --- a/common/redis_key/tm_member.go +++ b/common/redis_key/tm_member.go @@ -12,3 +12,8 @@ const ( //用户剩余翻译字符数 {key} 只有前缀 TM_MEMBER_REMAIN_COUNT_PURE = "tm_member_remain_count" ) + +const ( + //用户充值预订单 {amount} + TM_RECHARGE_PRE_ORDER = "tm_recharge_pre_order:%s" +) diff --git a/common/statuscode/status_1.go b/common/statuscode/status_1.go index aae6725..f044f01 100644 --- a/common/statuscode/status_1.go +++ b/common/statuscode/status_1.go @@ -10,19 +10,20 @@ type Response struct { } var ErrorMessage = map[int]string{ - Success: "success", - Unauthorized: "unauthorized", - ServerError: "server error", - NotFound: "not found", - Forbidden: "forbidden", - InvalidParams: "invalid params", - InSufficRemainChar: "insufficent remain char", - PlatformNotSupport: "platform not support", - TransactionNotAvailable: "transaction not available", - ApiUnauthorized: "api unauthorized", - NotFindMember: "not find member", - NotFindApiKey: "not find api key", - MemberPlatformNotSupport: "member platform not support", + Success: "success", + Unauthorized: "unauthorized", + ServerError: "server error", + NotFound: "not found", + Forbidden: "forbidden", + InvalidParams: "invalid params", + InSufficRemainChar: "insufficent remain char", + PlatformNotSupport: "platform not support", + TransactionNotAvailable: "transaction not available", + ApiUnauthorized: "api unauthorized", + NotFindMember: "not find member", + NotFindApiKey: "not find api key", + MemberPlatformNotSupport: "member platform not support", + RechargeAmountMustBeGreaterThanZero: "recharge amount must be greater than zero", } const ( @@ -44,4 +45,8 @@ const ( NotFindMember = 30001 //未找到用户 NotFindApiKey = 30002 //未找到api key MemberPlatformNotSupport = 30003 //用户平台不支持 + + //================ 充值相关 =============== + RechargeAmountMustBeGreaterThanZero = 40001 //充值金额必须大于0 + ) diff --git a/config/extend.go b/config/extend.go index d7cd20a..70c8451 100644 --- a/config/extend.go +++ b/config/extend.go @@ -10,8 +10,9 @@ var ExtConfig Extend // // 使用方法: config.ExtConfig......即可!! type Extend struct { - AMap AMap // 这里配置对应配置文件的结构即可 - Mq MqConfig + AMap AMap // 这里配置对应配置文件的结构即可 + Mq MqConfig + TrxGridUrl string `yaml:"trx_grid_url"` } type AMap struct { diff --git a/config/settings.yml b/config/settings.yml index 9769c79..10f82e2 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -53,6 +53,8 @@ settings: username: admin password: '123456' pass: "123456" + #trx api + trx_grid_url: "https://api.trongrid.io" cache: redis: addr: 127.0.0.1:6379 diff --git a/utils/utility/string_helper.go b/utils/utility/string_helper.go index 1fa4d2d..ef7c839 100644 --- a/utils/utility/string_helper.go +++ b/utils/utility/string_helper.go @@ -1,6 +1,10 @@ package utility -import "strings" +import ( + "strings" + + "github.com/shopspring/decimal" +) // DesensitizeGeneric 通用脱敏函数: // startReserveCount: 从字符串开头保留的字符数 @@ -61,3 +65,14 @@ func DesensitizeGeneric(text string, startReserveCount int, endReserveCount int, return sb.String() } + +func StringToDecimal(val string) decimal.Decimal { + cleanedNum := strings.TrimRight(val, "\x00") // 去除空字符 + cleanedNum = strings.TrimSpace(cleanedNum) // 去除空格 + cleanedNum = strings.ReplaceAll(cleanedNum, ",", "") // 去除逗号 + d, err := decimal.NewFromString(cleanedNum) + if err != nil { + return decimal.Zero + } + return d +}