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
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -21,3 +21,4 @@ cmd/migrate/migration/version-local/*
|
|||||||
# go sum
|
# go sum
|
||||||
go.sum
|
go.sum
|
||||||
config/settings.deva.yml
|
config/settings.deva.yml
|
||||||
|
/aggregate_translate_server
|
||||||
|
|||||||
Binary file not shown.
@ -222,3 +222,29 @@ func (e TmMemberPlatform) GetStatistic(c *gin.Context) {
|
|||||||
|
|
||||||
e.OK(data, "获取数据成功")
|
e.OK(data, "获取数据成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改用户-翻译通道字符消耗
|
||||||
|
func (e TmMemberPlatform) ChangeChars(c *gin.Context) {
|
||||||
|
s := service.TmMemberPlatform{}
|
||||||
|
req := dto.TmMemberPlatformChangeCharsReq{}
|
||||||
|
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.ChangeChars(&req, p)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, fmt.Sprintf("修改用户-翻译通道字符消耗失败,\r\n失败信息 %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.OK(nil, "修改成功")
|
||||||
|
}
|
||||||
|
|||||||
131
app/admin/apis/tm_recharge_log.go
Normal file
131
app/admin/apis/tm_recharge_log.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
package apis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-admin/app/admin/service"
|
||||||
|
"go-admin/app/admin/service/dto"
|
||||||
|
"go-admin/common/actions"
|
||||||
|
"go-admin/common/statuscode"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TmRechargeLog struct {
|
||||||
|
api.Api
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPage 分页查询
|
||||||
|
func (e TmRechargeLog) GetPage(c *gin.Context) {
|
||||||
|
s := service.TmRechargeLog{}
|
||||||
|
req := dto.TmRechargeLogGetPageReq{}
|
||||||
|
err := e.MakeContext(c).
|
||||||
|
MakeOrm().
|
||||||
|
Bind(&req).
|
||||||
|
MakeService(&s.Service).
|
||||||
|
Errors
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p := actions.GetPermissionFromContext(c)
|
||||||
|
datas := make([]dto.TmRechargeLogResp, 0)
|
||||||
|
var count int64
|
||||||
|
|
||||||
|
err = s.GetPage(&req, p, &datas, &count)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.PageOK(datas, int(count), req.GetPageIndex(), req.GetPageSize(), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建充值订单
|
||||||
|
func (e TmRechargeLog) CreateOrder(c *gin.Context) {
|
||||||
|
req := dto.TmRechargeCreateOrderReq{}
|
||||||
|
s := service.TmRechargeLog{}
|
||||||
|
err := e.MakeContext(c).
|
||||||
|
MakeOrm().
|
||||||
|
Bind(&req).
|
||||||
|
MakeService(&s.Service).
|
||||||
|
Errors
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, nil, statuscode.ErrorMessage[statuscode.ServerError])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiKey := c.GetString("apiKey")
|
||||||
|
|
||||||
|
code := s.CreateOrder(&req, apiKey)
|
||||||
|
if code != statuscode.Success {
|
||||||
|
e.OK(code, statuscode.ErrorMessage[code])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.OK(nil, "success")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后台充值
|
||||||
|
func (e TmRechargeLog) ManagerRecharge(c *gin.Context) {
|
||||||
|
req := dto.TmRechargeCreateOrderReq{}
|
||||||
|
s := service.TmRechargeLog{}
|
||||||
|
err := e.MakeContext(c).
|
||||||
|
MakeOrm().
|
||||||
|
Bind(&req).
|
||||||
|
MakeService(&s.Service).
|
||||||
|
Errors
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := req.Validate(); err != nil {
|
||||||
|
e.Error(500, err, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.SetCreateBy(user.GetUserId(c))
|
||||||
|
p := actions.GetPermissionFromContext(c)
|
||||||
|
|
||||||
|
err = s.ManagerRecharge(&req, p)
|
||||||
|
if err != nil {
|
||||||
|
e.Error(500, err, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.OK(nil, "充值成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取即将过期充值记录
|
||||||
|
func (e TmMember) GetMemberAdvent(c *gin.Context) {
|
||||||
|
s := service.TmRechargeLog{}
|
||||||
|
req := dto.TmRechargeLogFrontReq{}
|
||||||
|
err := e.MakeContext(c).
|
||||||
|
MakeOrm().
|
||||||
|
Bind(&req, binding.Query, binding.Form).
|
||||||
|
MakeService(&s.Service).
|
||||||
|
Errors
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Logger.Errorf("获取即将过期充值记录失败:", err)
|
||||||
|
e.Error(statuscode.ServerError, nil, statuscode.ErrorMessage[statuscode.ServerError])
|
||||||
|
}
|
||||||
|
|
||||||
|
userId := user.GetUserId(c)
|
||||||
|
datas := []dto.TmRechargeLogFrontResp{}
|
||||||
|
|
||||||
|
code := s.GetMemberAdvent(&req, &datas, userId)
|
||||||
|
if code != statuscode.Success {
|
||||||
|
e.Error(code, nil, statuscode.ErrorMessage[code])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.OK(datas, "success")
|
||||||
|
}
|
||||||
@ -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.Error(code, nil, statuscode.ErrorMessage[code])
|
e.OK(code, statuscode.ErrorMessage[code])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ type TmMemberPlatform struct {
|
|||||||
MemberId int `json:"memberId" gorm:"type:bigint;comment:用户id"`
|
MemberId int `json:"memberId" gorm:"type:bigint;comment:用户id"`
|
||||||
RemainingCharacter int `json:"remainingCharacter" gorm:"type:bigint;comment:剩余字符数"`
|
RemainingCharacter int `json:"remainingCharacter" gorm:"type:bigint;comment:剩余字符数"`
|
||||||
TotalCharacter int `json:"totalCharacter" gorm:"type:bigint;comment:总字符数"`
|
TotalCharacter int `json:"totalCharacter" gorm:"type:bigint;comment:总字符数"`
|
||||||
|
UseCharacter int `json:"useCharacter" gorm:"type:bigint;comment:已用字符数"`
|
||||||
PlatformId int `json:"platformId" gorm:"type:bigint;comment:平台id"`
|
PlatformId int `json:"platformId" gorm:"type:bigint;comment:平台id"`
|
||||||
PlatformKey string `json:"platformKey" gorm:"type:varchar(50);comment:平台key"`
|
PlatformKey string `json:"platformKey" gorm:"type:varchar(50);comment:平台key"`
|
||||||
Status int `json:"status" gorm:"type:tinyint;comment:状态 1-启用 2-禁用"`
|
Status int `json:"status" gorm:"type:tinyint;comment:状态 1-启用 2-禁用"`
|
||||||
|
|||||||
@ -1,14 +1,28 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "go-admin/common/models"
|
import (
|
||||||
|
"go-admin/common/models"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
type TmRechargeLog struct {
|
type TmRechargeLog struct {
|
||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
UserId int `json:"userId" gorm:"column:user_id;type:bigint;not null;comment:用户id"`
|
Type int `json:"type" gorm:"column:type;type:tinyint;not null;comment:类型 1-充值 2-后台充值 3-赠送"`
|
||||||
MemberId int `json:"memberId" gorm:"column:member_id;type:bigint;not null;comment:翻译用户id"`
|
OrderNo string `json:"orderNo" gorm:"column:order_no;type:varchar(36);not null;comment:订单号"`
|
||||||
Status int `json:"status" gorm:"column:status;type:tinyint;not null;comment:状态 1-正常 2-作废"`
|
UserId int `json:"userId" gorm:"column:user_id;type:bigint;not null;comment:用户id"`
|
||||||
TotalChars int `json:"totalChars" gorm:"column:total_chars;type:bigint;not null;comment:充值字符数"`
|
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:过期时间"`
|
||||||
models.ModelTime
|
models.ModelTime
|
||||||
models.ControlBy
|
models.ControlBy
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ func init() {
|
|||||||
// registerTmMemberRouter
|
// registerTmMemberRouter
|
||||||
func registerTmMemberRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
func registerTmMemberRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
||||||
api := apis.TmMember{}
|
api := apis.TmMember{}
|
||||||
|
rechargeApi := apis.TmRechargeLog{}
|
||||||
r := v1.Group("/tm-member").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
|
r := v1.Group("/tm-member").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
|
||||||
{
|
{
|
||||||
r.GET("", actions.PermissionAction(), api.GetPage)
|
r.GET("", actions.PermissionAction(), api.GetPage)
|
||||||
@ -24,7 +25,8 @@ func registerTmMemberRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl
|
|||||||
r.PUT("/:id", actions.PermissionAction(), api.Update)
|
r.PUT("/:id", actions.PermissionAction(), api.Update)
|
||||||
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.PUT("status", actions.PermissionAction(), api.ChangeStatus) //状态变更
|
r.PUT("status", actions.PermissionAction(), api.ChangeStatus) //状态变更
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,5 +34,6 @@ 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) //获取用户即将过期的充值信息
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ func registerTmMemberPlatformRouter(v1 *gin.RouterGroup, authMiddleware *jwt.Gin
|
|||||||
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.POST("/change-chars", actions.PermissionAction(), api.ChangeChars)
|
||||||
}
|
}
|
||||||
|
|
||||||
f2 := v1.Group("/tm-member-platform").Use(authMiddleware.MiddlewareFunc())
|
f2 := v1.Group("/tm-member-platform").Use(authMiddleware.MiddlewareFunc())
|
||||||
|
|||||||
@ -120,21 +120,24 @@ func (s *TmMemberDeleteReq) GetId() interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TmMemberResp struct {
|
type TmMemberResp struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
UserId int `json:"userId"`
|
UserId int `json:"userId"`
|
||||||
NickName string `json:"nickName"`
|
NickName string `json:"nickName"`
|
||||||
UserStatus int `json:"userStatus"`
|
UserStatus int `json:"userStatus"`
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
TotalChars int `json:"totalChars"`
|
TotalChars int `json:"totalChars"`
|
||||||
RemainChars int `json:"remainChars"`
|
RemainChars int `json:"remainChars"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
Platforms []TmMemberPlatformResp `json:"platforms"`
|
Platforms []TmMemberPlatformResp `json:"platforms"`
|
||||||
|
UsedPlatform []TmMemberPlatformResp `json:"usedPlatform"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TmMemberPlatformResp struct {
|
type TmMemberPlatformResp struct {
|
||||||
Name string `json:"name"`
|
PlatformId int `json:"platformId"`
|
||||||
RemainChars int `json:"remainChars"`
|
MemberId int `json:"memberId"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
TotalChars int `json:"totalChars"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TmMemberRechargeReq struct {
|
type TmMemberRechargeReq struct {
|
||||||
@ -175,9 +178,11 @@ func (e *TmMemberChangeStatusReq) Validate() error {
|
|||||||
|
|
||||||
type TmMemberPlatformFrontedResp struct {
|
type TmMemberPlatformFrontedResp struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
|
PlatformId int `json:"platformId"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
RemainChars int `json:"remainChars"`
|
RemainChars int `json:"remainChars"`
|
||||||
TotalChars int `json:"totalChars"`
|
TotalChars int `json:"totalChars"`
|
||||||
|
UseChars int `json:"useChars"`
|
||||||
Price int `json:"price"`
|
Price int `json:"price"`
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,3 +99,10 @@ type TmMemberDailyUsageDeleteReq struct {
|
|||||||
func (s *TmMemberDailyUsageDeleteReq) GetId() interface{} {
|
func (s *TmMemberDailyUsageDeleteReq) GetId() interface{} {
|
||||||
return s.Ids
|
return s.Ids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TmMemberPlatformGroupData struct {
|
||||||
|
MemberId int `json:"memberId" comment:"用户id"`
|
||||||
|
PlatformId int `json:"platformId" comment:"平台id"`
|
||||||
|
// PlatformKey string `json:"platformKey" comment:"平台key"`
|
||||||
|
Total int `json:"total" comment:"总使用量"`
|
||||||
|
}
|
||||||
|
|||||||
@ -109,3 +109,11 @@ type TmMemberPlatformStatisticItemResp struct {
|
|||||||
PlatformId int `json:"platformId"`
|
PlatformId int `json:"platformId"`
|
||||||
Data []int `json:"data" comment:"数据"`
|
Data []int `json:"data" comment:"数据"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TmMemberPlatformChangeCharsReq struct {
|
||||||
|
Type int `json:"type" comment:"1-可用字符,2-已使用字符"`
|
||||||
|
MemberId int `json:"memberId" comment:"用户id"`
|
||||||
|
PlatformId int `json:"platformId" comment:"平台id"`
|
||||||
|
ChangeNum int `json:"changeNum" comment:"变更数量"`
|
||||||
|
common.ControlBy
|
||||||
|
}
|
||||||
|
|||||||
165
app/admin/service/dto/tm_recharge_log.go
Normal file
165
app/admin/service/dto/tm_recharge_log.go
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"go-admin/app/admin/models"
|
||||||
|
"go-admin/common/dto"
|
||||||
|
common "go-admin/common/models"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TmRechargeLogGetPageReq struct {
|
||||||
|
dto.Pagination `search:"-"`
|
||||||
|
Type int `form:"type" search:"type:exact;column:type;table:tm_recharge_log"`
|
||||||
|
Status int `form:"status" search:"type:exact;column:status;table:tm_recharge_log"`
|
||||||
|
PlatformId int `form:"platformId" search:"type:exact;column:platform_id;table:tm_recharge_log"`
|
||||||
|
TmRechargeLogOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
type TmRechargeLogOrder struct {
|
||||||
|
Id string `form:"idOrder" search:"type:order;column:id;table:tm_platform"`
|
||||||
|
PayTime string `form:"payTimeOrder" search:"type:order;column:pay_time;table:tm_platform"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TmRechargeLogGetPageReq) GetNeedSearch() interface{} {
|
||||||
|
return *m
|
||||||
|
}
|
||||||
|
|
||||||
|
type TmRechargeLogInsertReq struct {
|
||||||
|
Id int `json:"-" comment:"平台id"`
|
||||||
|
MemberId int `json:"memberId" comment:"会员id"`
|
||||||
|
PlatformId int `json:"platformId" comment:"平台id"`
|
||||||
|
Amount decimal.Decimal `json:"amount" comment:"充值金额"`
|
||||||
|
TotalChars int `json:"totalChars" comment:"总字数"`
|
||||||
|
ReceiveChannel string `json:"receiveChannel" comment:"充值渠道"`
|
||||||
|
Status int `json:"status" comment:"充值状态"`
|
||||||
|
|
||||||
|
common.ControlBy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TmRechargeLogInsertReq) Generate(model *models.TmPlatform) {
|
||||||
|
if s.Id == 0 {
|
||||||
|
model.Model = common.Model{Id: s.Id}
|
||||||
|
}
|
||||||
|
|
||||||
|
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TmRechargeLogInsertReq) GetId() interface{} {
|
||||||
|
return s.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
type TmRechargeLogListResp struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
ShowName string `json:"showName"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Price decimal.Decimal `json:"price"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TmPlatformGetReq 功能获取请求参数
|
||||||
|
type TmRechargeLogGetReq struct {
|
||||||
|
Id int `uri:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TmRechargeLogGetReq) GetId() interface{} {
|
||||||
|
return s.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
// TmPlatformDeleteReq 功能删除请求参数
|
||||||
|
type TmRechargeLogDeleteReq struct {
|
||||||
|
Ids []int `json:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TmRechargeLogDeleteReq) GetId() interface{} {
|
||||||
|
return s.Ids
|
||||||
|
}
|
||||||
|
|
||||||
|
type TmRechargeLogResp struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
|
||||||
|
OrderNo string `json:"orderNo"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
MemberId int `json:"memberId"`
|
||||||
|
UserId int `json:"userId"`
|
||||||
|
PlatformId int `json:"platformId"`
|
||||||
|
Amount decimal.Decimal `json:"amount"`
|
||||||
|
TotalChars int `json:"totalChars"`
|
||||||
|
ReceiveChannel string `json:"receiveChannel"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
TxHash string `json:"txHash"`
|
||||||
|
PayTime string `json:"payTime"`
|
||||||
|
CreatedAt string `json:"createdAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户充值订单创建请求参数
|
||||||
|
type TmRechargeCreateOrderReq struct {
|
||||||
|
Type int `json:"type" comment:"充值类型 1-用户充值 2-平台充值"`
|
||||||
|
MemberId int `json:"memberId" comment:"会员id"`
|
||||||
|
UserId int `json:"userId" comment:"用户id"`
|
||||||
|
PlatformId int `json:"platformId" comment:"平台id"`
|
||||||
|
TotalChars int `json:"totalChars" comment:"总字数"`
|
||||||
|
ReceiveChannel string `json:"receiveChannel" comment:"充值渠道"`
|
||||||
|
ReceiveAddress string `json:"receiveAddress" comment:"充值地址"`
|
||||||
|
TxHash string `json:"txHash" comment:"交易hash"`
|
||||||
|
Amount decimal.Decimal `json:"amount" comment:"充值金额"`
|
||||||
|
ExpireDays int `json:"expireDays" comment:"过期天数"`
|
||||||
|
common.ControlBy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TmRechargeCreateOrderReq) Validate() error {
|
||||||
|
if e.ExpireDays <= 0 {
|
||||||
|
return errors.New("过期天数必须大于0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.MemberId <= 0 {
|
||||||
|
return errors.New("会员不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.PlatformId <= 0 {
|
||||||
|
return errors.New("平台不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TmRechargeCreateOrderReq) Generate(model *models.TmRechargeLog) {
|
||||||
|
if model == nil {
|
||||||
|
model = &models.TmRechargeLog{}
|
||||||
|
}
|
||||||
|
|
||||||
|
model.PlatformId = e.PlatformId
|
||||||
|
model.MemberId = e.MemberId
|
||||||
|
model.UserId = e.UserId
|
||||||
|
model.Type = e.Type
|
||||||
|
model.TotalChars = e.TotalChars * 10000
|
||||||
|
model.Amount = e.Amount
|
||||||
|
model.Status = 1
|
||||||
|
model.ReceiveChannel = e.ReceiveChannel
|
||||||
|
model.ReceiveAddress = e.ReceiveAddress
|
||||||
|
model.TxHash = e.TxHash
|
||||||
|
model.CreateBy = e.CreateBy
|
||||||
|
model.CreatedAt = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户充值订单创建参数
|
||||||
|
type TmRechargeLogInsertOrUpdateReq struct {
|
||||||
|
MemberId int `json:"memberId" comment:"会员id"`
|
||||||
|
PlatformId int `json:"platformId" comment:"平台id"`
|
||||||
|
PlatformKey string `json:"platformKey" comment:"平台key"`
|
||||||
|
RemainCharater int `json:"remainChars" comment:"剩余字数"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TmRechargeLogFrontReq struct {
|
||||||
|
PlatformId int `json:"platformId" query:"platformId" form:"platformId" comment:"平台id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TmRechargeLogFrontResp struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
OrderNo string `json:"orderNo"`
|
||||||
|
PlatformName string `json:"platformName"`
|
||||||
|
ExpireUnix int64 `json:"expireUnix"`
|
||||||
|
TotalCharater int `json:"totalCharater"`
|
||||||
|
RemainCharater int `json:"remainCharater"`
|
||||||
|
}
|
||||||
57
app/admin/service/quota_manager/lua/deduct.lua
Normal file
57
app/admin/service/quota_manager/lua/deduct.lua
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
-- KEYS[1] = tm_member_remain_count:{key}:{platformCode}
|
||||||
|
-- ARGV[1] = 当前时间戳(秒)
|
||||||
|
-- ARGV[2] = 扣减数量
|
||||||
|
|
||||||
|
local zset_key = KEYS[1]
|
||||||
|
local now = tonumber(ARGV[1])
|
||||||
|
local deduct = tonumber(ARGV[2])
|
||||||
|
local remain = deduct
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
|
||||||
|
-- 获取所有过期的 quota_id
|
||||||
|
local expired_quota_ids = redis.call("ZRANGEBYSCORE", zset_key, "-inf", now)
|
||||||
|
for _, quota_id in ipairs(expired_quota_ids) do
|
||||||
|
redis.call("DEL", "quota:" .. quota_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 清理过期额度
|
||||||
|
redis.call("ZREMRANGEBYSCORE", zset_key, "-inf", now)
|
||||||
|
|
||||||
|
local quota_ids = redis.call("ZRANGEBYSCORE", zset_key, now, "+inf")
|
||||||
|
|
||||||
|
for _, quota_id in ipairs(quota_ids) do
|
||||||
|
local quota_key = "quota:" .. quota_id
|
||||||
|
local amount = tonumber(redis.call("HGET", quota_key, "amount") or "0")
|
||||||
|
|
||||||
|
if amount > 0 then
|
||||||
|
local used = 0
|
||||||
|
if amount >= remain then
|
||||||
|
used = remain
|
||||||
|
redis.call("HINCRBY", quota_key, "amount", -remain)
|
||||||
|
remain = 0
|
||||||
|
else
|
||||||
|
used = amount
|
||||||
|
redis.call("HINCRBY", quota_key, "amount", -amount)
|
||||||
|
remain = remain - amount
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(result, quota_id .. ":" .. used)
|
||||||
|
|
||||||
|
local new_amount = tonumber(redis.call("HGET", quota_key, "amount") or "0")
|
||||||
|
if new_amount <= 0 then
|
||||||
|
redis.call("DEL", quota_key)
|
||||||
|
redis.call("ZREM", zset_key, quota_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if remain == 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if remain > 0 then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
21
app/admin/service/quota_manager/lua/refund.lua
Normal file
21
app/admin/service/quota_manager/lua/refund.lua
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
-- KEYS[1] = tm_member_remain_count:{key}:{platformCode}
|
||||||
|
-- ARGV = {quota_id_1, amount_1, expire_1, quota_id_2, amount_2, expire_2, ...}
|
||||||
|
|
||||||
|
local zset_key = KEYS[1]
|
||||||
|
local len = table.getn(ARGV)
|
||||||
|
|
||||||
|
for i = 1, len, 3 do
|
||||||
|
local quota_id = ARGV[i]
|
||||||
|
local amount = tonumber(ARGV[i+1])
|
||||||
|
local expire = tonumber(ARGV[i+2])
|
||||||
|
local quota_key = "quota:" .. quota_id
|
||||||
|
|
||||||
|
if redis.call("EXISTS", quota_key) == 0 then
|
||||||
|
redis.call("HSET", quota_key, "amount", amount)
|
||||||
|
redis.call("ZADD", zset_key, expire, quota_id)
|
||||||
|
else
|
||||||
|
redis.call("HINCRBY", quota_key, "amount", amount)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return 1
|
||||||
229
app/admin/service/quota_manager/quota_manager.go
Normal file
229
app/admin/service/quota_manager/quota_manager.go
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
package quota_manager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
rediskey "go-admin/common/redis_key"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QuotaUsage struct {
|
||||||
|
QuotaID string
|
||||||
|
Used int64
|
||||||
|
Expire int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuotaRecharge struct {
|
||||||
|
QuotaID string
|
||||||
|
Amount int64
|
||||||
|
ExpireAt int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuotaManager struct {
|
||||||
|
rdb *redis.Client
|
||||||
|
deductLua *redis.Script
|
||||||
|
refundLua *redis.Script
|
||||||
|
zsetKeyPrefix string // "tm_member_remain_count"
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQuotaManager(rdb *redis.Client) *QuotaManager {
|
||||||
|
deductScript := `
|
||||||
|
-- Lua 脚本内容复制 deduct.lua
|
||||||
|
local zset_key = KEYS[1]
|
||||||
|
local now = tonumber(ARGV[1])
|
||||||
|
local deduct = tonumber(ARGV[2])
|
||||||
|
local remain = deduct
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
local expired_quota_ids = redis.call("ZRANGEBYSCORE", zset_key, "-inf", now)
|
||||||
|
for _, quota_id in ipairs(expired_quota_ids) do
|
||||||
|
redis.call("DEL", "quota:" .. quota_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
redis.call("ZREMRANGEBYSCORE", zset_key, "-inf", now)
|
||||||
|
|
||||||
|
local quota_ids = redis.call("ZRANGEBYSCORE", zset_key, now, "+inf")
|
||||||
|
|
||||||
|
for _, quota_id in ipairs(quota_ids) do
|
||||||
|
local quota_key = "quota:" .. quota_id
|
||||||
|
local amount = tonumber(redis.call("HGET", quota_key, "amount") or "0")
|
||||||
|
|
||||||
|
if amount > 0 then
|
||||||
|
local used = 0
|
||||||
|
if amount >= remain then
|
||||||
|
used = remain
|
||||||
|
redis.call("HINCRBY", quota_key, "amount", -remain)
|
||||||
|
remain = 0
|
||||||
|
else
|
||||||
|
used = amount
|
||||||
|
redis.call("HINCRBY", quota_key, "amount", -amount)
|
||||||
|
remain = remain - amount
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(result, quota_id .. ":" .. used)
|
||||||
|
|
||||||
|
local new_amount = tonumber(redis.call("HGET", quota_key, "amount") or "0")
|
||||||
|
if new_amount <= 0 then
|
||||||
|
redis.call("DEL", quota_key)
|
||||||
|
redis.call("ZREM", zset_key, quota_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if remain == 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if remain > 0 then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
`
|
||||||
|
refundScript := `
|
||||||
|
-- Lua 脚本内容复制 refund.lua
|
||||||
|
local zset_key = KEYS[1]
|
||||||
|
local len = table.getn(ARGV)
|
||||||
|
|
||||||
|
for i = 1, len, 3 do
|
||||||
|
local quota_id = ARGV[i]
|
||||||
|
local amount = tonumber(ARGV[i+1])
|
||||||
|
local expire = tonumber(ARGV[i+2])
|
||||||
|
local quota_key = "quota:" .. quota_id
|
||||||
|
|
||||||
|
if redis.call("EXISTS", quota_key) == 0 then
|
||||||
|
redis.call("HSET", quota_key, "amount", amount)
|
||||||
|
redis.call("ZADD", zset_key, expire, quota_id)
|
||||||
|
else
|
||||||
|
redis.call("HINCRBY", quota_key, "amount", amount)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return 1
|
||||||
|
`
|
||||||
|
|
||||||
|
return &QuotaManager{
|
||||||
|
rdb: rdb,
|
||||||
|
deductLua: redis.NewScript(deductScript),
|
||||||
|
refundLua: redis.NewScript(refundScript),
|
||||||
|
zsetKeyPrefix: rediskey.TM_MEMBER_REMAIN_COUNT_PURE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QuotaManager) zsetKey(userKey, platformCode string) string {
|
||||||
|
return fmt.Sprintf("%s:%s:%s", q.zsetKeyPrefix, userKey, platformCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 充值写入
|
||||||
|
func (q *QuotaManager) AddQuota(ctx context.Context, userKey, platformCode string, recharge QuotaRecharge) error {
|
||||||
|
zsetKey := q.zsetKey(userKey, platformCode)
|
||||||
|
quotaKey := fmt.Sprintf("quota:%s", recharge.QuotaID)
|
||||||
|
|
||||||
|
err := q.rdb.HSet(ctx, quotaKey, map[string]interface{}{
|
||||||
|
"amount": recharge.Amount,
|
||||||
|
}).Err()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.rdb.ZAdd(ctx, zsetKey, &redis.Z{
|
||||||
|
Score: float64(recharge.ExpireAt),
|
||||||
|
Member: recharge.QuotaID,
|
||||||
|
}).Err()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扣减
|
||||||
|
func (q *QuotaManager) Deduct(ctx context.Context, userKey, platformCode string, deductCount int64) ([]QuotaUsage, error) {
|
||||||
|
zsetKey := q.zsetKey(userKey, platformCode)
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
res, err := q.deductLua.Run(ctx, q.rdb, []string{zsetKey}, now, deductCount).Result()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
arr, ok := res.([]interface{})
|
||||||
|
if !ok || len(arr) == 0 {
|
||||||
|
return nil, fmt.Errorf("insufficient quota or invalid response")
|
||||||
|
}
|
||||||
|
|
||||||
|
var usages []QuotaUsage
|
||||||
|
for _, v := range arr {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Split(s, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
used, _ := strconv.ParseInt(parts[1], 10, 64)
|
||||||
|
usages = append(usages, QuotaUsage{
|
||||||
|
QuotaID: parts[0],
|
||||||
|
Used: used,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range usages {
|
||||||
|
score, err := q.rdb.ZScore(ctx, zsetKey, usages[i].QuotaID).Result()
|
||||||
|
if err == nil {
|
||||||
|
usages[i].Expire = int64(score)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return usages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回滚
|
||||||
|
func (q *QuotaManager) Refund(ctx context.Context, userKey, platformCode string, usages []QuotaUsage) error {
|
||||||
|
zsetKey := q.zsetKey(userKey, platformCode)
|
||||||
|
|
||||||
|
var args []interface{}
|
||||||
|
for _, u := range usages {
|
||||||
|
args = append(args, u.QuotaID, u.Used, u.Expire)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := q.refundLua.Run(ctx, q.rdb, []string{zsetKey}, args...).Result()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QuotaManager) GetTotalRemainingQuota(ctx context.Context, apiKey, platformCode string) (int, error) {
|
||||||
|
zsetKey := q.zsetKey(apiKey, platformCode)
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
total := 0
|
||||||
|
|
||||||
|
// 获取全部 quotaID 和过期时间
|
||||||
|
zsetEntries, err := q.rdb.ZRangeWithScores(ctx, zsetKey, 0, -1).Result()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range zsetEntries {
|
||||||
|
expireAt := int64(entry.Score)
|
||||||
|
if expireAt <= now {
|
||||||
|
continue // 跳过已过期的 quota
|
||||||
|
}
|
||||||
|
|
||||||
|
quotaID := fmt.Sprintf("%v", entry.Member)
|
||||||
|
quotaKey := fmt.Sprintf("quota:%s", quotaID)
|
||||||
|
|
||||||
|
amountStr, err := q.rdb.HGet(ctx, quotaKey, "amount").Result()
|
||||||
|
if err != nil || amountStr == "" {
|
||||||
|
continue // 不存在或异常
|
||||||
|
}
|
||||||
|
|
||||||
|
amount, err := strconv.Atoi(amountStr)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
total += amount
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
80
app/admin/service/quota_manager/quota_manager_test.go
Normal file
80
app/admin/service/quota_manager/quota_manager_test.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package quota_manager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"go-admin/utils/redishelper"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
redisAddr = "localhost:6379"
|
||||||
|
redisPwd = ""
|
||||||
|
redisDB = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupRedis() {
|
||||||
|
redishelper.InitDefaultRedis(redisAddr, redisPwd, redisDB)
|
||||||
|
redishelper.InitLockRedisConn(redisAddr, redisPwd, strconv.Itoa(redisDB))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQuotaManager_AddDeductRefund(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
setupRedis()
|
||||||
|
qmgr := NewQuotaManager(redishelper.DefaultRedis.GetClient())
|
||||||
|
|
||||||
|
userKey := "Bw4iSj9Y90ix0e05GrMFp6EuFFTIbE9j"
|
||||||
|
platformCode := "deepl_free"
|
||||||
|
quotaID := "testquota1"
|
||||||
|
expireAt := time.Now().Add(1 * time.Hour).Unix()
|
||||||
|
amount := int64(1000)
|
||||||
|
|
||||||
|
// 充值写入
|
||||||
|
err := qmgr.AddQuota(ctx, userKey, platformCode, QuotaRecharge{
|
||||||
|
QuotaID: quotaID,
|
||||||
|
Amount: amount,
|
||||||
|
ExpireAt: expireAt,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("AddQuota failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扣减500
|
||||||
|
usages, err := qmgr.Deduct(ctx, userKey, platformCode, 500)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Deduct failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(usages) == 0 {
|
||||||
|
t.Fatal("Deduct returned empty usage")
|
||||||
|
}
|
||||||
|
|
||||||
|
if usages[0].Used != 500 {
|
||||||
|
t.Errorf("Expected Used=500, got %d", usages[0].Used)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回滚500
|
||||||
|
err = qmgr.Refund(ctx, userKey, platformCode, usages)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Refund failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再次扣减1000,确认余额充足
|
||||||
|
usages, err = qmgr.Deduct(ctx, userKey, platformCode, 1000)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Second Deduct failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置过期时间
|
||||||
|
func TestQuotaManager_Deduct_Refund_WithExpire(t *testing.T) {
|
||||||
|
setupRedis()
|
||||||
|
if err := redishelper.DefaultRedis.ZUpdateScore("tm_member_remain_count:MiyJrgfh3gYhwyDO43fdhHNswm4CeAfn:deepl", 1751436958, "573610499514565142"); err != nil {
|
||||||
|
t.Fatalf("ZUpdateScore failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
@ -82,7 +82,7 @@ func (e *SysUser) Insert(c *dto.SysUserInsertReq) error {
|
|||||||
e.Orm.Model(role).Where("role_id = ?", c.RoleId).Find(&role)
|
e.Orm.Model(role).Where("role_id = ?", c.RoleId).Find(&role)
|
||||||
|
|
||||||
if role.RoleId == 0 {
|
if role.RoleId == 0 {
|
||||||
err = errors.New("角色不存在")
|
return errors.New("角色不存在")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = e.Orm.Model(&data).Where("username = ?", c.Username).Count(&i).Error
|
err = e.Orm.Model(&data).Where("username = ?", c.Username).Count(&i).Error
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -9,12 +10,12 @@ import (
|
|||||||
|
|
||||||
"github.com/bytedance/sonic"
|
"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/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"go-admin/app/admin/models"
|
"go-admin/app/admin/models"
|
||||||
"go-admin/app/admin/service/dto"
|
"go-admin/app/admin/service/dto"
|
||||||
|
"go-admin/app/admin/service/quota_manager"
|
||||||
"go-admin/common/actions"
|
"go-admin/common/actions"
|
||||||
cDto "go-admin/common/dto"
|
cDto "go-admin/common/dto"
|
||||||
rediskey "go-admin/common/redis_key"
|
rediskey "go-admin/common/redis_key"
|
||||||
@ -31,6 +32,7 @@ func (e TmMember) GetUserPlatforms(userId int, resp *[]dto.TmMemberPlatformFront
|
|||||||
var memberAccount models.TmMember
|
var memberAccount models.TmMember
|
||||||
|
|
||||||
if err := e.Orm.Model(&memberAccount).
|
if err := e.Orm.Model(&memberAccount).
|
||||||
|
Where("user_id=?", userId).
|
||||||
Find(&memberAccount).Error; err != nil {
|
Find(&memberAccount).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -51,8 +53,10 @@ func (e TmMember) GetUserPlatforms(userId int, resp *[]dto.TmMemberPlatformFront
|
|||||||
|
|
||||||
dataItem.ApiKey = memberAccount.ApiKey
|
dataItem.ApiKey = memberAccount.ApiKey
|
||||||
dataItem.Name = platform.ShowName
|
dataItem.Name = platform.ShowName
|
||||||
|
dataItem.PlatformId = platform.Id
|
||||||
dataItem.Price = int(platform.Price.IntPart())
|
dataItem.Price = int(platform.Price.IntPart())
|
||||||
dataItem.RemainChars, _ = e.GetRemainCount(item.PlatformKey, dataItem.ApiKey)
|
dataItem.RemainChars = item.RemainingCharacter
|
||||||
|
dataItem.UseChars = item.UseCharacter
|
||||||
|
|
||||||
*resp = append(*resp, dataItem)
|
*resp = append(*resp, dataItem)
|
||||||
}
|
}
|
||||||
@ -82,25 +86,30 @@ func (e *TmMember) GetPage(c *dto.TmMemberGetPageReq, p *actions.DataPermission,
|
|||||||
}
|
}
|
||||||
|
|
||||||
userIds := []int{}
|
userIds := []int{}
|
||||||
|
memberIds := []int{}
|
||||||
|
|
||||||
for _, item := range datas {
|
for _, item := range datas {
|
||||||
if !utility.ContainsInt(userIds, item.UserId) {
|
if !utility.ContainsInt(userIds, item.UserId) {
|
||||||
userIds = append(userIds, item.UserId)
|
userIds = append(userIds, item.UserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !utility.ContainsInt(memberIds, item.Id) {
|
||||||
|
memberIds = append(memberIds, item.Id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memberPlatformService := TmMemberPlatform{Service: e.Service}
|
||||||
platformService := TmPlatform{Service: e.Service}
|
platformService := TmPlatform{Service: e.Service}
|
||||||
userService := SysUser{Service: e.Service}
|
userService := SysUser{Service: e.Service}
|
||||||
users, _ := userService.GetByIds(userIds)
|
users, _ := userService.GetByIds(userIds)
|
||||||
activeList, _ := platformService.GetActiveList()
|
activeList, _ := platformService.GetActiveList()
|
||||||
|
memberPlatforms, _ := memberPlatformService.GetMemberList(memberIds)
|
||||||
|
|
||||||
for _, item := range datas {
|
for _, item := range datas {
|
||||||
dataItem := dto.TmMemberResp{}
|
dataItem := dto.TmMemberResp{}
|
||||||
copier.Copy(&dataItem, &item)
|
copier.Copy(&dataItem, &item)
|
||||||
|
|
||||||
// count, _ := e.GetRemainCount(,dataItem.ApiKey)
|
|
||||||
dataItem.ApiKey = utility.DesensitizeGeneric(dataItem.ApiKey, 2, 2, '*')
|
dataItem.ApiKey = utility.DesensitizeGeneric(dataItem.ApiKey, 2, 2, '*')
|
||||||
// dataItem.RemainChars = count
|
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
if user.UserId == item.UserId {
|
if user.UserId == item.UserId {
|
||||||
@ -108,14 +117,33 @@ func (e *TmMember) GetPage(c *dto.TmMemberGetPageReq, p *actions.DataPermission,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//剩余字符
|
||||||
for _, platform := range activeList {
|
for _, platform := range activeList {
|
||||||
platformItem := dto.TmMemberPlatformResp{}
|
platformItem := dto.TmMemberPlatformResp{}
|
||||||
|
platformItem.PlatformId = platform.Id
|
||||||
|
platformItem.MemberId = item.Id
|
||||||
platformItem.Name = platform.Name
|
platformItem.Name = platform.Name
|
||||||
platformItem.RemainChars, _ = e.GetRemainCount(platform.Code, item.ApiKey)
|
platformItem.TotalChars, _ = e.GetRemainCount(platform.Code, item.ApiKey)
|
||||||
|
|
||||||
dataItem.Platforms = append(dataItem.Platforms, platformItem)
|
dataItem.Platforms = append(dataItem.Platforms, platformItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//已用字符
|
||||||
|
for _, platform := range activeList {
|
||||||
|
platformItem := dto.TmMemberPlatformResp{}
|
||||||
|
platformItem.PlatformId = platform.Id
|
||||||
|
platformItem.MemberId = item.Id
|
||||||
|
platformItem.Name = platform.Name
|
||||||
|
|
||||||
|
for _, memberPlatform := range memberPlatforms {
|
||||||
|
if memberPlatform.PlatformId == platform.Id && memberPlatform.MemberId == item.Id {
|
||||||
|
platformItem.TotalChars = memberPlatform.UseCharacter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataItem.UsedPlatform = append(dataItem.UsedPlatform, platformItem)
|
||||||
|
}
|
||||||
|
|
||||||
*list = append(*list, dataItem)
|
*list = append(*list, dataItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,13 +218,11 @@ func (e *TmMember) SaveAllCache() error {
|
|||||||
}
|
}
|
||||||
for _, item := range list {
|
for _, item := range list {
|
||||||
key := fmt.Sprintf(rediskey.TM_MEMBER_BY_KEY, item.ApiKey)
|
key := fmt.Sprintf(rediskey.TM_MEMBER_BY_KEY, item.ApiKey)
|
||||||
// remainKey := fmt.Sprintf(rediskey.TM_MEMBER_REMAIN_COUNT, item.ApiKey)
|
|
||||||
val, err := sonic.MarshalString(item)
|
val, err := sonic.MarshalString(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redishelper.DefaultRedis.SetString(key, val)
|
redishelper.DefaultRedis.SetString(key, val)
|
||||||
// redishelper.DefaultRedis.SetString(remainKey, strconv.Itoa(item.RemainChars))
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -262,64 +288,101 @@ func (e *TmMember) RemoveByUserIds(userIds []int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncMemberRemain 同步剩余字符
|
// SyncMemberRemain 同步剩余字符(ZSET 多笔充值版)
|
||||||
func (e *TmMember) SyncMemberRemain() error {
|
func (e *TmMember) SyncMemberRemain() error {
|
||||||
scanKeys, err := redishelper.DefaultRedis.ScanKeys(fmt.Sprintf("%s*", rediskey.TM_MEMBER_REMAIN_COUNT_PURE))
|
scanKeys, err := redishelper.DefaultRedis.ScanKeys(fmt.Sprintf("%s:*:*", rediskey.TM_MEMBER_REMAIN_COUNT_PURE))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
members := e.getCacheMembers()
|
members := e.getCacheMembers()
|
||||||
|
dailyUsageService := TmMemberDailyUsage{Service: e.Service}
|
||||||
datas := make([]models.TmMemberPlatform, 0)
|
datas := make([]models.TmMemberPlatform, 0)
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
for _, key := range scanKeys {
|
for _, key := range scanKeys {
|
||||||
items := strings.Split(key, ":")
|
items := strings.Split(key, ":")
|
||||||
apiKey := items[len(items)-2]
|
if len(items) < 3 {
|
||||||
platform := items[len(items)-1]
|
|
||||||
val, err := redishelper.DefaultRedis.GetString(key)
|
|
||||||
remainCount, err1 := strconv.Atoi(val)
|
|
||||||
|
|
||||||
if err != nil || err1 != nil {
|
|
||||||
e.Log.Errorf("TmMemberService SyncMemberRemain GetString error:%s \r\n err1:%s \r\n", err, err1)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apiKey := items[len(items)-2]
|
||||||
|
platform := items[len(items)-1]
|
||||||
|
|
||||||
|
// 清除过期的 quotaID(ZSET + Hash)
|
||||||
|
expiredIDs, err := redishelper.DefaultRedis.ZRangeByScore(key, "-inf", strconv.FormatInt(now, 10))
|
||||||
|
if err == nil && len(expiredIDs) > 0 {
|
||||||
|
for _, quotaID := range expiredIDs {
|
||||||
|
redishelper.DefaultRedis.DeleteString(fmt.Sprintf("quota:%s", quotaID))
|
||||||
|
}
|
||||||
|
redishelper.DefaultRedis.ZRemValues(key, expiredIDs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 ZSET 获取所有 quota ID(不清理过期,数据库中保留的是理论值)
|
||||||
|
zsetEntries, err := redishelper.DefaultRedis.GetRevRangeScoresSortSet(key)
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("TmMemberService SyncMemberRemain ZRANGE failed for key %s: %v", key, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
totalRemain := 0
|
||||||
|
for _, entry := range zsetEntries {
|
||||||
|
quotaID, ok := entry.Member.(string)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
quotaKey := fmt.Sprintf("quota:%s", quotaID)
|
||||||
|
amountStr, err := redishelper.DefaultRedis.HGetField(quotaKey, "amount")
|
||||||
|
if err != nil || amountStr == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
amount, err := strconv.Atoi(amountStr)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
totalRemain += amount
|
||||||
|
}
|
||||||
|
|
||||||
if member := members[apiKey]; member.Id > 0 {
|
if member := members[apiKey]; member.Id > 0 {
|
||||||
item := models.TmMemberPlatform{}
|
memberPlatform := models.TmMemberPlatform{
|
||||||
item.Id = member.Id
|
PlatformKey: platform,
|
||||||
item.RemainingCharacter = remainCount
|
RemainingCharacter: totalRemain,
|
||||||
item.PlatformKey = platform
|
}
|
||||||
datas = append(datas, item)
|
memberPlatform.Id = member.Id
|
||||||
|
datas = append(datas, memberPlatform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量更新数据库(分批,每批最多 1000 条)
|
||||||
arrayData := utility.SplitSlice(datas, 1000)
|
arrayData := utility.SplitSlice(datas, 1000)
|
||||||
for _, dataBatch := range arrayData {
|
for _, dataBatch := range arrayData {
|
||||||
|
|
||||||
// 遍历当前批次的所有记录,为每条记录单独执行 UPDATE
|
|
||||||
for _, record := range dataBatch {
|
for _, record := range dataBatch {
|
||||||
stmt := `
|
stmt := `
|
||||||
UPDATE tm_member_platform
|
UPDATE tm_member_platform
|
||||||
SET
|
SET
|
||||||
remaining_character = ?,
|
remaining_character = ?,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
WHERE platform_key = ? AND member_id = ?;
|
WHERE platform_key = ? AND member_id = ?;
|
||||||
`
|
`
|
||||||
args := []interface{}{
|
args := []interface{}{
|
||||||
record.RemainingCharacter,
|
record.RemainingCharacter,
|
||||||
record.PlatformKey,
|
record.PlatformKey,
|
||||||
record.Id,
|
record.Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行单个 UPDATE 语句
|
|
||||||
if err := e.Orm.Exec(stmt, args...).Error; err != nil {
|
if err := e.Orm.Exec(stmt, args...).Error; err != nil {
|
||||||
// 记录错误,但继续处理批次中的其他记录
|
e.Log.Errorf("TmMemberService SyncMemberRemain single Exec for PlatformKey %s, MemberID %d error: %s \r\n",
|
||||||
e.Log.Errorf("TmMemberService SyncMemberRemain single Exec for PlatformKey %s, MemberID %d error: %s \r\n", record.PlatformKey, record.Id, err)
|
record.PlatformKey, record.Id, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 同步每日使用量(不变)
|
||||||
|
dailyUsageService.SyncTotalUse()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,24 +507,6 @@ func (e *TmMember) loadSyncData(keys []string, members *map[string]models.TmMemb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (e *TmMember) GetMyApiKey(userId int) (dto.TranslateUserInfoResp, error) {
|
|
||||||
// var data models.TmMember
|
|
||||||
// resp := dto.TranslateUserInfoResp{}
|
|
||||||
// if err := e.Orm.Model(&data).Where("user_id = ?", userId).First(&data).Error; err != nil {
|
|
||||||
// e.Log.Errorf("TmMemberService GetMyApiKey error:%s \r\n", err)
|
|
||||||
// return resp, nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var err error
|
|
||||||
// resp.UserApiKey = data.ApiKey
|
|
||||||
// resp.RemainChars, err = e.GetRemainCount(data.ApiKey)
|
|
||||||
|
|
||||||
// if err != nil {
|
|
||||||
// e.Log.Errorf("转换类型失败,error:%v", err)
|
|
||||||
// }
|
|
||||||
// return resp, nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// GetTranslateStatistic 获取翻译统计
|
// GetTranslateStatistic 获取翻译统计
|
||||||
func (e *TmMember) GetTranslateStatistic(userId int, list *[]dto.TranslateStatisticResp) error {
|
func (e *TmMember) GetTranslateStatistic(userId int, list *[]dto.TranslateStatisticResp) error {
|
||||||
endDate := time.Now().Format("2006-01-02")
|
endDate := time.Now().Format("2006-01-02")
|
||||||
@ -535,31 +580,24 @@ func (e *TmMember) Recharge(req *dto.TmMemberRechargeReq, p *actions.DataPermiss
|
|||||||
|
|
||||||
// 获取可用字符数
|
// 获取可用字符数
|
||||||
func (e *TmMember) GetRemainCount(platformKey, apiKey string) (int, error) {
|
func (e *TmMember) GetRemainCount(platformKey, apiKey string) (int, error) {
|
||||||
key := fmt.Sprintf(rediskey.TM_MEMBER_REMAIN_COUNT, apiKey, platformKey)
|
ctx := context.Background()
|
||||||
val, err := redishelper.DefaultRedis.GetString(key)
|
quotaManager := quota_manager.NewQuotaManager(redishelper.DefaultRedis.GetClient())
|
||||||
result := 0
|
result, err := quotaManager.GetTotalRemainingQuota(ctx, apiKey, platformKey)
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, redis.Nil) {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if val != "" {
|
// if result == 0 {
|
||||||
result, err = strconv.Atoi(val)
|
// var data models.TmMember
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if result == 0 {
|
// if err := e.Orm.Model(&data).Where("api_key = ?", apiKey).First(&data).Error; err != nil {
|
||||||
var data models.TmMember
|
// return 0, err
|
||||||
|
// }
|
||||||
|
|
||||||
if err := e.Orm.Model(&data).Where("api_key = ?", apiKey).First(&data).Error; err != nil {
|
// result = data.RemainChars
|
||||||
return 0, err
|
// // redishelper.DefaultRedis.SetString(key, strconv.Itoa(result))
|
||||||
}
|
// }
|
||||||
|
|
||||||
result = data.RemainChars
|
|
||||||
redishelper.DefaultRedis.SetString(key, strconv.Itoa(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
@ -578,6 +616,55 @@ func (e *TmMember) DecrBy(platformKey, apiKey string, totalChars int) error {
|
|||||||
return redishelper.DefaultRedis.DecrBy(remainCountKey, int64(totalChars)).Err()
|
return redishelper.DefaultRedis.DecrBy(remainCountKey, int64(totalChars)).Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 增加字符
|
||||||
|
func (e *TmMember) IncyByQuote(ctx context.Context, platformKey, apiKey string, totalChars int, orderNo string, expireAt time.Time) error {
|
||||||
|
quotaManager := quota_manager.NewQuotaManager(redishelper.DefaultRedis.GetClient())
|
||||||
|
rechargeData := quota_manager.QuotaRecharge{
|
||||||
|
QuotaID: orderNo,
|
||||||
|
Amount: int64(totalChars),
|
||||||
|
ExpireAt: expireAt.Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quotaManager.AddQuota(ctx, apiKey, platformKey, rechargeData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扣除字符
|
||||||
|
func (e *TmMember) DecrByQuote(ctx context.Context, platformKey, apiKey string, totalChars int) ([]quota_manager.QuotaUsage, error) {
|
||||||
|
quoteManage := quota_manager.NewQuotaManager(redishelper.DefaultRedis.GetClient())
|
||||||
|
|
||||||
|
datas, err := quoteManage.Deduct(ctx, apiKey, platformKey, int64(totalChars))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return datas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回滚扣除字符
|
||||||
|
// apiKey 用户翻译密钥
|
||||||
|
// platformKey 平台标识
|
||||||
|
// datas 扣除的字符数据
|
||||||
|
func (e *TmMember) RefundQuote(ctx context.Context, apiKey, platformKey string, datas *[]quota_manager.QuotaUsage) error {
|
||||||
|
quoteManage := quota_manager.NewQuotaManager(redishelper.DefaultRedis.GetClient())
|
||||||
|
if err := quoteManage.Refund(ctx, apiKey, platformKey, *datas); err != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置可用字符数
|
||||||
|
func (e *TmMember) SetChars(platformKey, apiKey string, totalChars int) error {
|
||||||
|
remainCountKey := fmt.Sprintf(rediskey.TM_MEMBER_REMAIN_COUNT, apiKey, platformKey)
|
||||||
|
|
||||||
|
return redishelper.DefaultRedis.SetString(remainCountKey, strconv.Itoa(totalChars))
|
||||||
|
}
|
||||||
|
|
||||||
// 根据id获取数据
|
// 根据id获取数据
|
||||||
func (e *TmMember) GetById(id int, data *models.TmMember) error {
|
func (e *TmMember) GetById(id int, data *models.TmMember) error {
|
||||||
if err := e.Orm.Model(data).Where("id = ?", id).First(data).Error; err != nil {
|
if err := e.Orm.Model(data).Where("id = ?", id).First(data).Error; err != nil {
|
||||||
@ -586,6 +673,15 @@ func (e *TmMember) GetById(id int, data *models.TmMember) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据userId 获取数据
|
||||||
|
func (e *TmMember) GetByUserId(userId int, data *models.TmMember) error {
|
||||||
|
if err := e.Orm.Model(data).Where("user_id = ?", userId).First(data).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// 修改翻译用户状态
|
// 修改翻译用户状态
|
||||||
func (e TmMember) ChangeStatus(req *dto.TmMemberChangeStatusReq, p *actions.DataPermission) error {
|
func (e TmMember) ChangeStatus(req *dto.TmMemberChangeStatusReq, p *actions.DataPermission) error {
|
||||||
var err error
|
var err error
|
||||||
@ -638,7 +734,6 @@ func (e *TmMember) SyncInsert(req *dto.TmMemberSyncInsertReq, entity *models.TmM
|
|||||||
platformService := TmPlatform{Service: e.Service}
|
platformService := TmPlatform{Service: e.Service}
|
||||||
activePlatforms, _ := platformService.GetActiveList()
|
activePlatforms, _ := platformService.GetActiveList()
|
||||||
TmMemberPlatforms := make([]models.TmMemberPlatform, 0)
|
TmMemberPlatforms := make([]models.TmMemberPlatform, 0)
|
||||||
|
|
||||||
copier.Copy(entity, req)
|
copier.Copy(entity, req)
|
||||||
|
|
||||||
apiKey, err := utility.GenerateBase62Key(32)
|
apiKey, err := utility.GenerateBase62Key(32)
|
||||||
@ -661,8 +756,8 @@ func (e *TmMember) SyncInsert(req *dto.TmMemberSyncInsertReq, entity *models.TmM
|
|||||||
PlatformId: platform.Id,
|
PlatformId: platform.Id,
|
||||||
PlatformKey: platform.Code,
|
PlatformKey: platform.Code,
|
||||||
Status: 1,
|
Status: 1,
|
||||||
TotalCharacter: 10000,
|
TotalCharacter: 0,
|
||||||
RemainingCharacter: 10000,
|
RemainingCharacter: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
TmMemberPlatforms = append(TmMemberPlatforms, item)
|
TmMemberPlatforms = append(TmMemberPlatforms, item)
|
||||||
@ -672,9 +767,10 @@ func (e *TmMember) SyncInsert(req *dto.TmMemberSyncInsertReq, entity *models.TmM
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, platform := range TmMemberPlatforms {
|
// for _, platform := range TmMemberPlatforms {
|
||||||
e.IncrBy(platform.PlatformKey, entity.ApiKey, platform.RemainingCharacter)
|
// // e.IncrBy(platform.PlatformKey, entity.ApiKey, platform.RemainingCharacter)
|
||||||
}
|
// e.IncyByQuote(ctx,platform.PlatformKey,entity.ApiKey,platform.TotalCharacter,,)
|
||||||
|
// }
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,7 +128,7 @@ func (e *TmMemberDailyUsage) GetStatistic(userId int, resp *dto.TmMemberPlatform
|
|||||||
|
|
||||||
if err := e.Orm.Model(models.TmMemberDailyUsage{}).
|
if err := e.Orm.Model(models.TmMemberDailyUsage{}).
|
||||||
Joins("JOIN tm_member on tm_member.id=tm_member_daily_usage.member_id").
|
Joins("JOIN tm_member on tm_member.id=tm_member_daily_usage.member_id").
|
||||||
Where("tm_member_daily_usage.date >= ? and tm_member_daily_usage.date <= ?", startTime, endTime).Find(&datas).Error; err != nil {
|
Where("tm_member.user_id =? and tm_member_daily_usage.date >= ? and tm_member_daily_usage.date <= ?", userId, startTime, endTime).Find(&datas).Error; err != nil {
|
||||||
e.Log.Error("获取折线图数据失败", err)
|
e.Log.Error("获取折线图数据失败", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -178,3 +178,24 @@ func (e *TmMemberDailyUsage) GetStatistic(userId int, resp *dto.TmMemberPlatform
|
|||||||
resp.Data = respDatas
|
resp.Data = respDatas
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 同步总使用量
|
||||||
|
func (e *TmMemberDailyUsage) SyncTotalUse() error {
|
||||||
|
var datas []dto.TmMemberPlatformGroupData
|
||||||
|
var data models.TmMemberPlatform
|
||||||
|
|
||||||
|
if err := e.Orm.Model(models.TmMemberDailyUsage{}).
|
||||||
|
Group("member_id, platform_id").
|
||||||
|
Select("member_id, platform_id, sum(use_chars) as total").
|
||||||
|
Find(&datas).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range datas {
|
||||||
|
if err := e.Orm.Model(data).Where("member_id = ? and platform_id = ? ", item.MemberId, item.PlatformId).Update("use_character", item.Total).Error; err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -16,6 +16,42 @@ type TmMemberPlatform struct {
|
|||||||
service.Service
|
service.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改用户字符
|
||||||
|
func (e TmMemberPlatform) ChangeChars(req *dto.TmMemberPlatformChangeCharsReq, p *actions.DataPermission) error {
|
||||||
|
var data models.TmMemberPlatform
|
||||||
|
|
||||||
|
switch req.Type {
|
||||||
|
case 1:
|
||||||
|
member := models.TmMember{}
|
||||||
|
memberService := TmMember{Service: e.Service}
|
||||||
|
memberService.GetById(req.MemberId, &member)
|
||||||
|
|
||||||
|
if member.Id == 0 {
|
||||||
|
return errors.New("用户不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
platformService := TmPlatform{Service: e.Service}
|
||||||
|
platform, err := platformService.GetById(req.PlatformId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("平台不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := memberService.SetChars(platform.Code, member.ApiKey, req.ChangeNum); err != nil {
|
||||||
|
return errors.New("设置剩余字符失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.Orm.Model(data).Where("member_id =? AND platform_id =?", req.MemberId, req.PlatformId).Update("remaining_character", req.ChangeNum).Error; err != nil {
|
||||||
|
e.Log.Errorf("TmMemberPlatformService ChangeChars error:%s \r\n", err)
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
default:
|
||||||
|
return errors.New("修改类型错误")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetPage 获取TmMemberPlatform列表
|
// GetPage 获取TmMemberPlatform列表
|
||||||
func (e *TmMemberPlatform) GetPage(c *dto.TmMemberPlatformGetPageReq, p *actions.DataPermission, list *[]models.TmMemberPlatform, count *int64) error {
|
func (e *TmMemberPlatform) GetPage(c *dto.TmMemberPlatformGetPageReq, p *actions.DataPermission, list *[]models.TmMemberPlatform, count *int64) error {
|
||||||
var err error
|
var err error
|
||||||
@ -107,3 +143,40 @@ func (e *TmMemberPlatform) Remove(d *dto.TmMemberPlatformDeleteReq, p *actions.D
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMemberList 获取用户列表
|
||||||
|
func (e *TmMemberPlatform) GetMemberList(memberIds []int) ([]models.TmMemberPlatform, error) {
|
||||||
|
result := make([]models.TmMemberPlatform, 0)
|
||||||
|
err := e.Orm.Model(&models.TmMemberPlatform{}).
|
||||||
|
Where("member_id IN (?)", memberIds).
|
||||||
|
Find(&result).Error
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("Service GetMemberList error:%s \r\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertOrUpdateRemainChars 新增或更新用户剩余字符
|
||||||
|
func (e *TmMemberPlatform) GetOrInsert(req *dto.TmRechargeLogInsertOrUpdateReq) (models.TmMemberPlatform, error) {
|
||||||
|
result := models.TmMemberPlatform{}
|
||||||
|
|
||||||
|
e.Orm.Model(result).Where("member_id =? AND platform_id =?", req.MemberId, req.PlatformId).First(&result)
|
||||||
|
|
||||||
|
if result.Id == 0 {
|
||||||
|
result.MemberId = req.MemberId
|
||||||
|
result.PlatformId = req.PlatformId
|
||||||
|
result.PlatformKey = req.PlatformKey
|
||||||
|
result.RemainingCharacter = 0
|
||||||
|
result.TotalCharacter = 0
|
||||||
|
result.Status = 1
|
||||||
|
|
||||||
|
if err := e.Orm.Save(&result).Error; err != nil {
|
||||||
|
e.Log.Errorf("Service InsertOrUpdateRemainChars error:%s \r\n", err)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -225,6 +225,17 @@ func (e *TmPlatform) GetByKey(code string) (*models.TmPlatform, error) {
|
|||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据id 获取翻译平台信息
|
||||||
|
func (e *TmPlatform) GetById(id int) (*models.TmPlatform, error) {
|
||||||
|
var result models.TmPlatform
|
||||||
|
err := e.Orm.Model(&result).Where("id = ?", id).Find(&result).Error
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("db error:%s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *TmPlatform) GetActiveList() ([]models.TmPlatform, error) {
|
func (e *TmPlatform) GetActiveList() ([]models.TmPlatform, error) {
|
||||||
var list []models.TmPlatform
|
var list []models.TmPlatform
|
||||||
err := e.Orm.Model(&models.TmPlatform{}).Find(&list).Error
|
err := e.Orm.Model(&models.TmPlatform{}).Find(&list).Error
|
||||||
|
|||||||
205
app/admin/service/tm_recharge_log.go
Normal file
205
app/admin/service/tm_recharge_log.go
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go-admin/app/admin/models"
|
||||||
|
"go-admin/app/admin/service/dto"
|
||||||
|
"go-admin/app/admin/service/quota_manager"
|
||||||
|
"go-admin/common/actions"
|
||||||
|
cDto "go-admin/common/dto"
|
||||||
|
"go-admin/common/statuscode"
|
||||||
|
"go-admin/utils/redishelper"
|
||||||
|
"go-admin/utils/utility"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-admin-team/go-admin-core/sdk/service"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TmRechargeLog struct {
|
||||||
|
service.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首页获取即将过期的数据
|
||||||
|
func (e *TmRechargeLog) GetMemberAdvent(req *dto.TmRechargeLogFrontReq, resp *[]dto.TmRechargeLogFrontResp, userId int) int {
|
||||||
|
var datas []models.TmRechargeLog
|
||||||
|
|
||||||
|
if err := e.Orm.Model(&models.TmRechargeLog{}).
|
||||||
|
Where("user_id =? and platform_id =? and expire_at > now() and status =2", userId, req.PlatformId).
|
||||||
|
Find(&datas).Error; err != nil {
|
||||||
|
e.Log.Errorf("TmRechargeLogService GetMemberAdvent error:%s \r\n", err)
|
||||||
|
return statuscode.ServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range datas {
|
||||||
|
respItem := dto.TmRechargeLogFrontResp{}
|
||||||
|
respItem.Id = item.Id
|
||||||
|
respItem.OrderNo = item.OrderNo
|
||||||
|
// respItem.PlatformName=item.PlatformName
|
||||||
|
respItem.TotalCharater = item.TotalChars
|
||||||
|
respItem.ExpireUnix = item.ExpireAt.Unix()
|
||||||
|
count, err := e.GetRemainByOrderNo(item.OrderNo)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("TmRechargeLogService GetRemainByOrderNo error:%s \r\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
respItem.RemainCharater = count
|
||||||
|
*resp = append(*resp, respItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(*resp, func(i, j int) bool {
|
||||||
|
return (*resp)[i].ExpireUnix < (*resp)[j].ExpireUnix
|
||||||
|
})
|
||||||
|
|
||||||
|
return statuscode.Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后台充值
|
||||||
|
func (e *TmRechargeLog) ManagerRecharge(req *dto.TmRechargeCreateOrderReq, p *actions.DataPermission) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
var data models.TmRechargeLog
|
||||||
|
member, platform, memberPlatform, code := e.CreateOrderJudge(req)
|
||||||
|
if code != statuscode.Success {
|
||||||
|
return errors.New(statuscode.ErrorMessage[code])
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Generate(&data)
|
||||||
|
now := time.Now()
|
||||||
|
data.OrderNo = utility.GenerateTraceID()
|
||||||
|
data.Type = 2
|
||||||
|
data.Status = 2
|
||||||
|
data.UserId = member.UserId
|
||||||
|
data.ExpireAt = time.Now().AddDate(0, 0, req.ExpireDays)
|
||||||
|
data.PayTime = &now
|
||||||
|
|
||||||
|
rechargeData := quota_manager.QuotaRecharge{
|
||||||
|
QuotaID: data.OrderNo,
|
||||||
|
Amount: int64(data.TotalChars),
|
||||||
|
ExpireAt: data.ExpireAt.Unix(),
|
||||||
|
}
|
||||||
|
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 := tx.Model(&models.TmMemberPlatform{}).Where("id =?", memberPlatform.Id).Update("remaining_character", data.TotalChars).Error; err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
//写入可用字符
|
||||||
|
if err1 := qmgr.AddQuota(ctx, member.ApiKey, platform.Code, rechargeData); err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增充值校验
|
||||||
|
func (e *TmRechargeLog) CreateOrderJudge(req *dto.TmRechargeCreateOrderReq) (models.TmMember, *models.TmPlatform, models.TmMemberPlatform, int) {
|
||||||
|
memberService := TmMember{Service: e.Service}
|
||||||
|
member := models.TmMember{}
|
||||||
|
if err := memberService.GetById(req.MemberId, &member); err != nil {
|
||||||
|
return models.TmMember{}, nil, models.TmMemberPlatform{}, statuscode.NotFindMember
|
||||||
|
}
|
||||||
|
|
||||||
|
if member.ApiKey == "" {
|
||||||
|
return models.TmMember{}, nil, models.TmMemberPlatform{}, statuscode.NotFindApiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
platformService := TmPlatform{Service: e.Service}
|
||||||
|
platform, err := platformService.GetById(req.PlatformId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("获取平台信息失败:%s \r\n", err.Error())
|
||||||
|
return models.TmMember{}, nil, models.TmMemberPlatform{}, statuscode.PlatformNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
if platform == nil || platform.Id == 0 {
|
||||||
|
return models.TmMember{}, nil, models.TmMemberPlatform{}, statuscode.PlatformNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
memberPlatformService := TmMemberPlatform{Service: e.Service}
|
||||||
|
memberPlatform, err := memberPlatformService.GetOrInsert(&dto.TmRechargeLogInsertOrUpdateReq{
|
||||||
|
MemberId: req.MemberId,
|
||||||
|
PlatformId: req.PlatformId,
|
||||||
|
PlatformKey: platform.Code,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return models.TmMember{}, nil, models.TmMemberPlatform{}, statuscode.ServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
if memberPlatform.Status == 2 {
|
||||||
|
return models.TmMember{}, nil, models.TmMemberPlatform{}, statuscode.MemberPlatformNotSupport
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
return statuscode.Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页查询
|
||||||
|
func (e *TmRechargeLog) GetPage(req *dto.TmRechargeLogGetPageReq, p *actions.DataPermission, datas *[]dto.TmRechargeLogResp, count *int64) error {
|
||||||
|
var err error
|
||||||
|
var data models.TmRechargeLog
|
||||||
|
var list []models.TmRechargeLog
|
||||||
|
|
||||||
|
err = e.Orm.Model(&data).
|
||||||
|
Scopes(
|
||||||
|
cDto.MakeCondition(req.GetNeedSearch()),
|
||||||
|
cDto.Paginate(req.GetPageSize(), req.GetPageIndex()),
|
||||||
|
actions.Permission(data.TableName(), p),
|
||||||
|
).
|
||||||
|
Find(list).Limit(-1).Offset(-1).
|
||||||
|
Count(count).Error
|
||||||
|
if err != nil {
|
||||||
|
e.Log.Errorf("TmPlatformService GetPage error:%s \r\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据充值订单号 获取剩余可用字符
|
||||||
|
func (e *TmRechargeLog) GetRemainByOrderNo(orderNo string) (int, error) {
|
||||||
|
key := fmt.Sprintf("quota:%s", orderNo)
|
||||||
|
val, err := redishelper.DefaultRedis.HGetField(key, "amount")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strconv.Atoi(val)
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go-admin/app/admin/models"
|
"go-admin/app/admin/models"
|
||||||
@ -95,6 +96,7 @@ func (s *TranslatorService) RegisterProvider(name string, provider Translator) {
|
|||||||
// 翻译校验
|
// 翻译校验
|
||||||
// return statusCode
|
// return statusCode
|
||||||
func (s *TranslatorService) TranslateJudge(req *dto.TranslateReq, apiKey string) (result *dto.TranslateResult, respCode int) {
|
func (s *TranslatorService) TranslateJudge(req *dto.TranslateReq, apiKey string) (result *dto.TranslateResult, respCode int) {
|
||||||
|
ctx := context.Background()
|
||||||
tmMemberService := TmMember{Service: s.Service}
|
tmMemberService := TmMember{Service: s.Service}
|
||||||
tmPlatformAccount := TmPlatformAccount{Service: s.Service}
|
tmPlatformAccount := TmPlatformAccount{Service: s.Service}
|
||||||
memberInfo, err1 := tmMemberService.GetByKey(apiKey)
|
memberInfo, err1 := tmMemberService.GetByKey(apiKey)
|
||||||
@ -126,16 +128,23 @@ func (s *TranslatorService) TranslateJudge(req *dto.TranslateReq, apiKey string)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := Translator.providers[req.Platform].Translate(req.Text, req.SourceLang, req.TargetLang)
|
decyDatas, err := tmMemberService.DecrByQuote(ctx, req.Platform, apiKey, count)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("翻译计数失败:%v", err)
|
||||||
|
return nil, statuscode.ServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err = Translator.providers[req.Platform].Translate(req.Text, req.SourceLang, req.TargetLang)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err2 := tmMemberService.DecrBy(req.Platform, apiKey, count)
|
// err2 := tmMemberService.DecrBy(req.Platform, apiKey, count)
|
||||||
|
|
||||||
if err2 != nil {
|
// if err2 != nil {
|
||||||
s.Log.Errorf("翻译计数失败:%v", err2)
|
// s.Log.Errorf("翻译计数失败:%v", err2)
|
||||||
respCode = statuscode.ServerError
|
// respCode = statuscode.ServerError
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
platformConfigInterface := Translator.config.ProviderConfigs[req.Platform]
|
platformConfigInterface := Translator.config.ProviderConfigs[req.Platform]
|
||||||
|
|
||||||
@ -149,6 +158,8 @@ func (s *TranslatorService) TranslateJudge(req *dto.TranslateReq, apiKey string)
|
|||||||
//每日统计保留三天
|
//每日统计保留三天
|
||||||
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)
|
||||||
|
|
||||||
code = statuscode.ServerError
|
code = statuscode.ServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import (
|
|||||||
"go-admin/common/storage"
|
"go-admin/common/storage"
|
||||||
ext "go-admin/config"
|
ext "go-admin/config"
|
||||||
"go-admin/utils/redishelper"
|
"go-admin/utils/redishelper"
|
||||||
|
"go-admin/utils/utility"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -177,6 +178,9 @@ func run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initCommon() {
|
func initCommon() {
|
||||||
|
//初始化雪花算法
|
||||||
|
utility.InitSnowflake()
|
||||||
|
|
||||||
redishelper.InitDefaultRedis(config.CacheConfig.Redis.Addr, config.CacheConfig.Redis.Password, config.CacheConfig.Redis.DB)
|
redishelper.InitDefaultRedis(config.CacheConfig.Redis.Addr, config.CacheConfig.Redis.Password, config.CacheConfig.Redis.DB)
|
||||||
|
|
||||||
if err := redishelper.DefaultRedis.Ping(); err != nil {
|
if err := redishelper.DefaultRedis.Ping(); err != nil {
|
||||||
@ -195,6 +199,7 @@ func initCommon() {
|
|||||||
|
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var Router runtime.Router
|
var Router runtime.Router
|
||||||
|
|||||||
@ -10,16 +10,19 @@ type Response struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ErrorMessage = map[int]string{
|
var ErrorMessage = map[int]string{
|
||||||
Success: "success",
|
Success: "success",
|
||||||
Unauthorized: "unauthorized",
|
Unauthorized: "unauthorized",
|
||||||
ServerError: "server error",
|
ServerError: "server error",
|
||||||
NotFound: "not found",
|
NotFound: "not found",
|
||||||
Forbidden: "forbidden",
|
Forbidden: "forbidden",
|
||||||
InvalidParams: "invalid params",
|
InvalidParams: "invalid params",
|
||||||
InSufficRemainChar: "insufficent remain char",
|
InSufficRemainChar: "insufficent remain char",
|
||||||
PlatformNotSupport: "platform not support",
|
PlatformNotSupport: "platform not support",
|
||||||
TransactionNotAvailable: "transaction not available",
|
TransactionNotAvailable: "transaction not available",
|
||||||
ApiUnauthorized: "api unauthorized",
|
ApiUnauthorized: "api unauthorized",
|
||||||
|
NotFindMember: "not find member",
|
||||||
|
NotFindApiKey: "not find api key",
|
||||||
|
MemberPlatformNotSupport: "member platform not support",
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -37,4 +40,8 @@ const (
|
|||||||
PlatformNotSupport = 20002 //平台不支持
|
PlatformNotSupport = 20002 //平台不支持
|
||||||
TransactionNotAvailable = 20003 //翻译服务不可用
|
TransactionNotAvailable = 20003 //翻译服务不可用
|
||||||
ApiUnauthorized = 20004 //api禁止访问
|
ApiUnauthorized = 20004 //api禁止访问
|
||||||
|
|
||||||
|
NotFindMember = 30001 //未找到用户
|
||||||
|
NotFindApiKey = 30002 //未找到api key
|
||||||
|
MemberPlatformNotSupport = 30003 //用户平台不支持
|
||||||
)
|
)
|
||||||
|
|||||||
1
go.mod
1
go.mod
@ -126,6 +126,7 @@ require (
|
|||||||
github.com/shamsher31/goimgext v1.0.0 // indirect
|
github.com/shamsher31/goimgext v1.0.0 // indirect
|
||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||||
|
github.com/sony/sonyflake v1.2.1 // indirect
|
||||||
github.com/spf13/cast v1.3.1 // indirect
|
github.com/spf13/cast v1.3.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.3 // indirect
|
github.com/spf13/pflag v1.0.3 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.9 // indirect
|
github.com/tklauser/go-sysconf v0.3.9 // indirect
|
||||||
|
|||||||
@ -51,6 +51,14 @@ func NewRedisHelper(addr, password string, db int) *RedisHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RedisHelper) GetClient() *redis.Client {
|
||||||
|
return r.client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RedisHelper) GetCtx() context.Context {
|
||||||
|
return r.ctx
|
||||||
|
}
|
||||||
|
|
||||||
// 测试连接
|
// 测试连接
|
||||||
func (r *RedisHelper) Ping() error {
|
func (r *RedisHelper) Ping() error {
|
||||||
return r.client.Ping(r.ctx).Err()
|
return r.client.Ping(r.ctx).Err()
|
||||||
@ -100,6 +108,19 @@ func (r *RedisHelper) SetAdd(key, value string, expireTime time.Duration) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新zset score
|
||||||
|
func (r *RedisHelper) ZUpdateScore(key string, score float64, value string) error {
|
||||||
|
return r.client.ZAddArgs(r.ctx, key, redis.ZAddArgs{
|
||||||
|
XX: true, // 只更新已存在
|
||||||
|
Members: []redis.Z{
|
||||||
|
{
|
||||||
|
Score: float64(score),
|
||||||
|
Member: value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).Err()
|
||||||
|
}
|
||||||
|
|
||||||
// 设置对象
|
// 设置对象
|
||||||
func SetObjString[T any](r *RedisHelper, key string, value T) error {
|
func SetObjString[T any](r *RedisHelper, key string, value T) error {
|
||||||
keyValue, err := sonic.Marshal(value)
|
keyValue, err := sonic.Marshal(value)
|
||||||
@ -736,6 +757,28 @@ func (e *RedisHelper) GetRevRangeScoresSortSet(key string) ([]redis.Z, error) {
|
|||||||
return e.client.ZRevRangeWithScores(e.ctx, key, 0, -1).Result()
|
return e.client.ZRevRangeWithScores(e.ctx, key, 0, -1).Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ZSET 中按 score 范围取出成员
|
||||||
|
func (r *RedisHelper) ZRangeByScore(key string, min, max string) ([]string, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
return r.client.ZRangeByScore(ctx, key, &redis.ZRangeBy{
|
||||||
|
Min: min,
|
||||||
|
Max: max,
|
||||||
|
}).Result()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZSET 中移除指定成员:
|
||||||
|
func (r *RedisHelper) ZRemValues(key string, members ...string) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 转换为 interface{} 类型参数
|
||||||
|
vals := make([]interface{}, len(members))
|
||||||
|
for i, m := range members {
|
||||||
|
vals[i] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.client.ZRem(ctx, key, vals...).Err()
|
||||||
|
}
|
||||||
|
|
||||||
// 获取最后一条数据
|
// 获取最后一条数据
|
||||||
func (e *RedisHelper) GetLastSortSet(key string) ([]redis.Z, error) {
|
func (e *RedisHelper) GetLastSortSet(key string) ([]redis.Z, error) {
|
||||||
// 获取最后一个元素及其分数
|
// 获取最后一个元素及其分数
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package utility
|
package utility
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
@ -14,6 +15,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/go-admin-team/go-admin-core/logger"
|
log "github.com/go-admin-team/go-admin-core/logger"
|
||||||
|
"github.com/sony/sonyflake"
|
||||||
)
|
)
|
||||||
|
|
||||||
const base62Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
const base62Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
@ -79,3 +81,20 @@ func GenerateBase62Key(length int) (string, error) {
|
|||||||
|
|
||||||
return b.String(), nil
|
return b.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sf *sonyflake.Sonyflake
|
||||||
|
|
||||||
|
func InitSnowflake() {
|
||||||
|
sf = sonyflake.NewSonyflake(sonyflake.Settings{})
|
||||||
|
if sf == nil {
|
||||||
|
log.Fatalf("Failed to initialize sonyflake")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateTraceID() string {
|
||||||
|
id, err := sf.NextID()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to generate ID: %v", err)
|
||||||
|
}
|
||||||
|
return strconv.FormatUint(id, 10)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user