1
This commit is contained in:
49
common/actions/create.go
Normal file
49
common/actions/create.go
Normal file
@ -0,0 +1,49 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
|
||||
"go-admin/common/dto"
|
||||
"go-admin/common/models"
|
||||
)
|
||||
|
||||
// CreateAction 通用新增动作
|
||||
func CreateAction(control dto.Control) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
log := api.GetRequestLogger(c)
|
||||
db, err := pkg.GetOrm(c)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
//新增操作
|
||||
req := control.Generate()
|
||||
err = req.Bind(c)
|
||||
if err != nil {
|
||||
response.Error(c, http.StatusUnprocessableEntity, err, err.Error())
|
||||
return
|
||||
}
|
||||
var object models.ActiveRecord
|
||||
object, err = req.GenerateM()
|
||||
if err != nil {
|
||||
response.Error(c, 500, err, "模型生成失败")
|
||||
return
|
||||
}
|
||||
object.SetCreateBy(user.GetUserId(c))
|
||||
err = db.WithContext(c).Create(object).Error
|
||||
if err != nil {
|
||||
log.Errorf("Create error: %s", err)
|
||||
response.Error(c, 500, err, "创建失败")
|
||||
return
|
||||
}
|
||||
response.OK(c, object.GetId(), "创建成功")
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
61
common/actions/delete.go
Normal file
61
common/actions/delete.go
Normal file
@ -0,0 +1,61 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
|
||||
"go-admin/common/dto"
|
||||
"go-admin/common/models"
|
||||
)
|
||||
|
||||
// DeleteAction 通用删除动作
|
||||
func DeleteAction(control dto.Control) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
db, err := pkg.GetOrm(c)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
msgID := pkg.GenerateMsgIDFromContext(c)
|
||||
//删除操作
|
||||
req := control.Generate()
|
||||
err = req.Bind(c)
|
||||
if err != nil {
|
||||
log.Errorf("MsgID[%s] Bind error: %s", msgID, err)
|
||||
response.Error(c, http.StatusUnprocessableEntity, err, "参数验证失败")
|
||||
return
|
||||
}
|
||||
var object models.ActiveRecord
|
||||
object, err = req.GenerateM()
|
||||
if err != nil {
|
||||
response.Error(c, 500, err, "模型生成失败")
|
||||
return
|
||||
}
|
||||
|
||||
object.SetUpdateBy(user.GetUserId(c))
|
||||
|
||||
//数据权限检查
|
||||
p := GetPermissionFromContext(c)
|
||||
|
||||
db = db.WithContext(c).Scopes(
|
||||
Permission(object.TableName(), p),
|
||||
).Where(req.GetId()).Delete(object)
|
||||
if err = db.Error; err != nil {
|
||||
log.Errorf("MsgID[%s] Delete error: %s", msgID, err)
|
||||
response.Error(c, 500, err, "删除失败")
|
||||
return
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
response.Error(c, http.StatusForbidden, nil, "无权删除该数据")
|
||||
return
|
||||
}
|
||||
response.OK(c, object.GetId(), "删除成功")
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
58
common/actions/index.go
Normal file
58
common/actions/index.go
Normal file
@ -0,0 +1,58 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"go-admin/common/dto"
|
||||
"go-admin/common/models"
|
||||
)
|
||||
|
||||
// IndexAction 通用查询动作
|
||||
func IndexAction(m models.ActiveRecord, d dto.Index, f func() interface{}) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
db, err := pkg.GetOrm(c)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
msgID := pkg.GenerateMsgIDFromContext(c)
|
||||
list := f()
|
||||
object := m.Generate()
|
||||
req := d.Generate()
|
||||
var count int64
|
||||
|
||||
//查询列表
|
||||
err = req.Bind(c)
|
||||
if err != nil {
|
||||
response.Error(c, http.StatusUnprocessableEntity, err, "参数验证失败")
|
||||
return
|
||||
}
|
||||
|
||||
//数据权限检查
|
||||
p := GetPermissionFromContext(c)
|
||||
|
||||
err = db.WithContext(c).Model(object).
|
||||
Scopes(
|
||||
dto.MakeCondition(req.GetNeedSearch()),
|
||||
dto.Paginate(req.GetPageSize(), req.GetPageIndex()),
|
||||
Permission(object.TableName(), p),
|
||||
).
|
||||
Find(list).Limit(-1).Offset(-1).
|
||||
Count(&count).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
log.Errorf("MsgID[%s] Index error: %s", msgID, err)
|
||||
response.Error(c, 500, err, "查询失败")
|
||||
return
|
||||
}
|
||||
response.PageOK(c, list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
96
common/actions/permission.go
Normal file
96
common/actions/permission.go
Normal file
@ -0,0 +1,96 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/config"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type DataPermission struct {
|
||||
DataScope string
|
||||
UserId int
|
||||
DeptId int
|
||||
RoleId int
|
||||
}
|
||||
|
||||
func PermissionAction() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
db, err := pkg.GetOrm(c)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
msgID := pkg.GenerateMsgIDFromContext(c)
|
||||
var p = new(DataPermission)
|
||||
if userId := user.GetUserIdStr(c); userId != "" {
|
||||
p, err = newDataPermission(db, userId)
|
||||
if err != nil {
|
||||
log.Errorf("MsgID[%s] PermissionAction error: %s", msgID, err)
|
||||
response.Error(c, 500, err, "权限范围鉴定错误")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
c.Set(PermissionKey, p)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func newDataPermission(tx *gorm.DB, userId interface{}) (*DataPermission, error) {
|
||||
var err error
|
||||
p := &DataPermission{}
|
||||
|
||||
err = tx.Table("sys_user").
|
||||
Select("sys_user.user_id", "sys_role.role_id", "sys_user.dept_id", "sys_role.data_scope").
|
||||
Joins("left join sys_role on sys_role.role_id = sys_user.role_id").
|
||||
Where("sys_user.user_id = ?", userId).
|
||||
Scan(p).Error
|
||||
if err != nil {
|
||||
err = errors.New("获取用户数据出错 msg:" + err.Error())
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func Permission(tableName string, p *DataPermission) func(db *gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
if !config.ApplicationConfig.EnableDP {
|
||||
return db
|
||||
}
|
||||
switch p.DataScope {
|
||||
case "2":
|
||||
return db.Where(tableName+".create_by in (select sys_user.user_id from sys_role_dept left join sys_user on sys_user.dept_id=sys_role_dept.dept_id where sys_role_dept.role_id = ?)", p.RoleId)
|
||||
case "3":
|
||||
return db.Where(tableName+".create_by in (SELECT user_id from sys_user where dept_id = ? )", p.DeptId)
|
||||
case "4":
|
||||
return db.Where(tableName+".create_by in (SELECT user_id from sys_user where sys_user.dept_id in(select dept_id from sys_dept where dept_path like ? ))", "%/"+pkg.IntToString(p.DeptId)+"/%")
|
||||
case "5":
|
||||
return db.Where(tableName+".create_by = ?", p.UserId)
|
||||
default:
|
||||
return db
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getPermissionFromContext(c *gin.Context) *DataPermission {
|
||||
p := new(DataPermission)
|
||||
if pm, ok := c.Get(PermissionKey); ok {
|
||||
switch pm.(type) {
|
||||
case *DataPermission:
|
||||
p = pm.(*DataPermission)
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// GetPermissionFromContext 提供非action写法数据范围约束
|
||||
func GetPermissionFromContext(c *gin.Context) *DataPermission {
|
||||
return getPermissionFromContext(c)
|
||||
}
|
||||
5
common/actions/type.go
Normal file
5
common/actions/type.go
Normal file
@ -0,0 +1,5 @@
|
||||
package actions
|
||||
|
||||
const (
|
||||
PermissionKey = "dataPermission"
|
||||
)
|
||||
59
common/actions/update.go
Normal file
59
common/actions/update.go
Normal file
@ -0,0 +1,59 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
|
||||
"go-admin/common/dto"
|
||||
"go-admin/common/models"
|
||||
)
|
||||
|
||||
// UpdateAction 通用更新动作
|
||||
func UpdateAction(control dto.Control) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
db, err := pkg.GetOrm(c)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
msgID := pkg.GenerateMsgIDFromContext(c)
|
||||
req := control.Generate()
|
||||
//更新操作
|
||||
err = req.Bind(c)
|
||||
if err != nil {
|
||||
response.Error(c, http.StatusUnprocessableEntity, err, "参数验证失败")
|
||||
return
|
||||
}
|
||||
var object models.ActiveRecord
|
||||
object, err = req.GenerateM()
|
||||
if err != nil {
|
||||
response.Error(c, 500, err, "模型生成失败")
|
||||
return
|
||||
}
|
||||
object.SetUpdateBy(user.GetUserId(c))
|
||||
|
||||
//数据权限检查
|
||||
p := GetPermissionFromContext(c)
|
||||
|
||||
db = db.WithContext(c).Scopes(
|
||||
Permission(object.TableName(), p),
|
||||
).Where(req.GetId()).Updates(object)
|
||||
if err = db.Error; err != nil {
|
||||
log.Errorf("MsgID[%s] Update error: %s", msgID, err)
|
||||
response.Error(c, 500, err, "更新失败")
|
||||
return
|
||||
}
|
||||
if db.RowsAffected == 0 {
|
||||
response.Error(c, http.StatusForbidden, nil, "无权更新该数据")
|
||||
return
|
||||
}
|
||||
response.OK(c, object.GetId(), "更新成功")
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
67
common/actions/view.go
Normal file
67
common/actions/view.go
Normal file
@ -0,0 +1,67 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"go-admin/common/dto"
|
||||
"go-admin/common/models"
|
||||
)
|
||||
|
||||
// ViewAction 通用详情动作
|
||||
func ViewAction(control dto.Control, f func() interface{}) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
db, err := pkg.GetOrm(c)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
msgID := pkg.GenerateMsgIDFromContext(c)
|
||||
//查看详情
|
||||
req := control.Generate()
|
||||
err = req.Bind(c)
|
||||
if err != nil {
|
||||
response.Error(c, http.StatusUnprocessableEntity, err, "参数验证失败")
|
||||
return
|
||||
}
|
||||
var object models.ActiveRecord
|
||||
object, err = req.GenerateM()
|
||||
if err != nil {
|
||||
response.Error(c, 500, err, "模型生成失败")
|
||||
return
|
||||
}
|
||||
|
||||
var rsp interface{}
|
||||
if f != nil {
|
||||
rsp = f()
|
||||
} else {
|
||||
rsp, _ = req.GenerateM()
|
||||
}
|
||||
|
||||
//数据权限检查
|
||||
p := GetPermissionFromContext(c)
|
||||
|
||||
err = db.Model(object).WithContext(c).Scopes(
|
||||
Permission(object.TableName(), p),
|
||||
).Where(req.GetId()).First(rsp).Error
|
||||
|
||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
response.Error(c, http.StatusNotFound, nil, "查看对象不存在或无权查看")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("MsgID[%s] View error: %s", msgID, err)
|
||||
response.Error(c, 500, err, "查看失败")
|
||||
return
|
||||
}
|
||||
response.OK(c, rsp, "查询成功")
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
135
common/apis/api.go
Normal file
135
common/apis/api.go
Normal file
@ -0,0 +1,135 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"go-admin/common/service"
|
||||
)
|
||||
|
||||
type Api struct {
|
||||
Context *gin.Context
|
||||
Logger *logger.Helper
|
||||
Orm *gorm.DB
|
||||
Errors error
|
||||
}
|
||||
|
||||
func (e *Api) AddError(err error) {
|
||||
if e.Errors == nil {
|
||||
e.Errors = err
|
||||
} else if err != nil {
|
||||
e.Logger.Error(err)
|
||||
e.Errors = fmt.Errorf("%v; %w", e.Errors, err)
|
||||
}
|
||||
}
|
||||
|
||||
// MakeContext 设置http上下文
|
||||
func (e *Api) MakeContext(c *gin.Context) *Api {
|
||||
e.Context = c
|
||||
e.Logger = api.GetRequestLogger(c)
|
||||
return e
|
||||
}
|
||||
|
||||
// GetLogger 获取上下文提供的日志
|
||||
func (e *Api) GetLogger() *logger.Helper {
|
||||
return api.GetRequestLogger(e.Context)
|
||||
}
|
||||
|
||||
func (e *Api) Bind(d interface{}, bindings ...binding.Binding) *Api {
|
||||
var err error
|
||||
if len(bindings) == 0 {
|
||||
bindings = append(bindings, binding.JSON, nil)
|
||||
}
|
||||
for i := range bindings {
|
||||
switch bindings[i] {
|
||||
case binding.JSON:
|
||||
err = e.Context.ShouldBindWith(d, binding.JSON)
|
||||
case binding.XML:
|
||||
err = e.Context.ShouldBindWith(d, binding.XML)
|
||||
case binding.Form:
|
||||
err = e.Context.ShouldBindWith(d, binding.Form)
|
||||
case binding.Query:
|
||||
err = e.Context.ShouldBindWith(d, binding.Query)
|
||||
case binding.FormPost:
|
||||
err = e.Context.ShouldBindWith(d, binding.FormPost)
|
||||
case binding.FormMultipart:
|
||||
err = e.Context.ShouldBindWith(d, binding.FormMultipart)
|
||||
case binding.ProtoBuf:
|
||||
err = e.Context.ShouldBindWith(d, binding.ProtoBuf)
|
||||
case binding.MsgPack:
|
||||
err = e.Context.ShouldBindWith(d, binding.MsgPack)
|
||||
case binding.YAML:
|
||||
err = e.Context.ShouldBindWith(d, binding.YAML)
|
||||
case binding.Header:
|
||||
err = e.Context.ShouldBindWith(d, binding.Header)
|
||||
default:
|
||||
err = e.Context.ShouldBindUri(d)
|
||||
}
|
||||
if err != nil {
|
||||
e.AddError(err)
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// GetOrm 获取Orm DB
|
||||
func (e *Api) GetOrm() (*gorm.DB, error) {
|
||||
db, err := pkg.GetOrm(e.Context)
|
||||
if err != nil {
|
||||
e.Error(500, err, "数据库连接获取失败")
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// MakeOrm 设置Orm DB
|
||||
func (e *Api) MakeOrm() *Api {
|
||||
var err error
|
||||
if e.Logger == nil {
|
||||
err = errors.New("at MakeOrm logger is nil")
|
||||
//e.Logger.Error(500, err, "at MakeOrm logger is nil")
|
||||
e.AddError(err)
|
||||
return e
|
||||
}
|
||||
db, err := pkg.GetOrm(e.Context)
|
||||
if err != nil {
|
||||
e.Logger.Error(500, err, "数据库连接获取失败")
|
||||
e.AddError(err)
|
||||
}
|
||||
e.Orm = db
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Api) MakeService(c *service.Service) *Api {
|
||||
c.Log = e.Logger
|
||||
c.Orm = e.Orm
|
||||
return e
|
||||
}
|
||||
|
||||
// Error 通常错误数据处理
|
||||
func (e *Api) Error(code int, err error, msg string) {
|
||||
response.Error(e.Context, code, err, msg)
|
||||
}
|
||||
|
||||
// OK 通常成功数据处理
|
||||
func (e *Api) OK(data interface{}, msg string) {
|
||||
response.OK(e.Context, data, msg)
|
||||
}
|
||||
|
||||
// PageOK 分页数据处理
|
||||
func (e *Api) PageOK(result interface{}, count int, pageIndex int, pageSize int, msg string) {
|
||||
response.PageOK(e.Context, result, count, pageIndex, pageSize, msg)
|
||||
}
|
||||
|
||||
// Custom 兼容函数
|
||||
func (e *Api) Custom(data gin.H) {
|
||||
response.Custum(e.Context, data)
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package ad_userinfo_transferopen
|
||||
|
||||
// 用户是否开启内部转账功能
|
||||
const (
|
||||
Close = 1 // 关闭
|
||||
Open = 3 // 开启
|
||||
)
|
||||
@ -0,0 +1,6 @@
|
||||
package agent_account_source
|
||||
|
||||
const (
|
||||
UserRegister = 1 // 用户注册
|
||||
SystemRegister = 3 // 系统添加
|
||||
)
|
||||
@ -0,0 +1,7 @@
|
||||
package agent_account_status
|
||||
|
||||
const (
|
||||
Wait = 1 // 待审核
|
||||
Audited = 3 // 已通过
|
||||
Reject = 5 // 已拒绝
|
||||
)
|
||||
@ -0,0 +1,6 @@
|
||||
package agent_reward_category
|
||||
|
||||
const (
|
||||
Straight = 1 // 直客佣金
|
||||
Agent = 3 // 代理佣金
|
||||
)
|
||||
96
common/const/enum/businesstype/businesstype.go
Normal file
96
common/const/enum/businesstype/businesstype.go
Normal file
@ -0,0 +1,96 @@
|
||||
package businesstype
|
||||
|
||||
// BusinessType 操作业务
|
||||
type BusinessType int
|
||||
|
||||
const (
|
||||
// 注册登录业务
|
||||
Other BusinessType = 0 // 其他
|
||||
Register BusinessType = 1 // 注册
|
||||
Login BusinessType = 2 // 登入
|
||||
ResetPass BusinessType = 3 // 找回密码
|
||||
ChangePassword BusinessType = 4 // 更改密码
|
||||
ScanLogin BusinessType = 14 // 扫码登入
|
||||
|
||||
// 身份验证专用业务类型(请勿占用)
|
||||
OpenPhoneAuth BusinessType = 11
|
||||
ChangePhoneAuth BusinessType = 21
|
||||
ClosePhoneAuth BusinessType = 31
|
||||
OpenEmailAuth BusinessType = 12
|
||||
ChangeEmailAuth BusinessType = 22
|
||||
CloseEmailAuth BusinessType = 32
|
||||
OpenGoogleAuth BusinessType = 13
|
||||
ChangeGoogleAuth BusinessType = 23
|
||||
CloseGoogleAuth BusinessType = 33
|
||||
|
||||
//提现验证使用
|
||||
WithdrawAuth BusinessType = 201
|
||||
//userkey验证
|
||||
UserKeyAuth BusinessType = 202
|
||||
//地址浦
|
||||
AddAddress BusinessType = 203
|
||||
|
||||
// 以下请从 100 开始定义
|
||||
OtcCaptcha BusinessType = 100 // OTC 验证码
|
||||
AddPayAccount BusinessType = 101 // 新增收款账号
|
||||
EditPayAccount BusinessType = 102 // 编辑收款账号
|
||||
OTCSellerConfirmPay BusinessType = 103 // OTC 卖家已确认收款
|
||||
OTCSellerAppeal BusinessType = 104 // OTC 买家申诉
|
||||
OTCBuyerPaid BusinessType = 105 // OTC 买家已付款
|
||||
OTCBuyerAppeal BusinessType = 106 // OTC 买家申诉
|
||||
AgentEmailCode BusinessType = 107 // 代理商找回密码验证码
|
||||
)
|
||||
|
||||
// 业务类型中文映射
|
||||
var BusinessTypeMapCN = map[BusinessType]string{
|
||||
Other: "其它",
|
||||
Register: "注册",
|
||||
Login: "登录",
|
||||
ResetPass: "重置密码",
|
||||
ChangePassword: "修改登录密码",
|
||||
|
||||
OpenPhoneAuth: "开启手机验证",
|
||||
ChangePhoneAuth: "更改手机验证",
|
||||
ClosePhoneAuth: "关闭手机验证",
|
||||
OpenEmailAuth: "开启邮箱验证",
|
||||
ChangeEmailAuth: "更改邮箱验证",
|
||||
CloseEmailAuth: "关闭邮箱验证",
|
||||
OpenGoogleAuth: "开启谷歌验证",
|
||||
ChangeGoogleAuth: "更改谷歌验证",
|
||||
CloseGoogleAuth: "关闭谷歌验证",
|
||||
|
||||
WithdrawAuth: "提现验证",
|
||||
UserKeyAuth: "api验证",
|
||||
AddAddress: "新增地址浦验证",
|
||||
|
||||
OtcCaptcha: "OTC验证码",
|
||||
AddPayAccount: "新增收款账号",
|
||||
EditPayAccount: "编辑收款账号",
|
||||
}
|
||||
|
||||
// 业务类型英文映射
|
||||
var BusinessTypeMapEN = map[BusinessType]string{
|
||||
Other: "Other",
|
||||
Register: "Register",
|
||||
Login: "Login",
|
||||
ResetPass: "Reset Password",
|
||||
ChangePassword: "Change Login Password",
|
||||
|
||||
OpenPhoneAuth: "Enable mobile phone verification",
|
||||
ChangePhoneAuth: "Change phone verification",
|
||||
ClosePhoneAuth: "Turn off phone verification",
|
||||
OpenEmailAuth: "Enable mailbox verification",
|
||||
ChangeEmailAuth: "Change mailbox validation",
|
||||
CloseEmailAuth: "Turn off mailbox verification",
|
||||
OpenGoogleAuth: "Turn on Google Authentication",
|
||||
ChangeGoogleAuth: "Change Google Authentication",
|
||||
CloseGoogleAuth: "Turn off Google Authentication",
|
||||
|
||||
WithdrawAuth: "Withdraw Auth",
|
||||
UserKeyAuth: "UserKey Auth",
|
||||
AddAddress: "AddAddress Auth",
|
||||
|
||||
OtcCaptcha: "otc phone verification",
|
||||
AddPayAccount: "otc add payaccount",
|
||||
EditPayAccount: "otc edit payaccount",
|
||||
}
|
||||
8
common/const/enum/cointype/cointype.go
Normal file
8
common/const/enum/cointype/cointype.go
Normal file
@ -0,0 +1,8 @@
|
||||
package cointype
|
||||
|
||||
// cointype
|
||||
const (
|
||||
BTC int = 1 // BTC
|
||||
USDT int = 2 // USDT
|
||||
USD int = 3 // USD
|
||||
)
|
||||
10
common/const/enum/culatortype/calculatortype.go
Normal file
10
common/const/enum/culatortype/calculatortype.go
Normal file
@ -0,0 +1,10 @@
|
||||
package culatortype
|
||||
|
||||
// CulatorType 计算器
|
||||
const (
|
||||
Income int = 1 // 收益
|
||||
TargetPrice int = 2 // 目标价格
|
||||
FlatPrice int = 3 // 强平价格
|
||||
OpenEable int = 4 // 可开
|
||||
OpenPrice int = 5 // 开仓价格
|
||||
)
|
||||
39
common/const/enum/dealtype/dealtype.go
Normal file
39
common/const/enum/dealtype/dealtype.go
Normal file
@ -0,0 +1,39 @@
|
||||
package dealtype
|
||||
|
||||
// DealType 资金小类型:1买单,2卖单,3手续费,4划转(废弃),5已实现盈亏,6爆仓精算,7资金费用 13内部转入 14 内部转出 101现货划转合约 102 合约划转现货 103 现货转法币 104 法币转现货 105 币本位划转现货 106 现货划转到币本位
|
||||
// 说明: 13-106之间不能插入其他数值
|
||||
const (
|
||||
Buy int = 1 // 1买单
|
||||
Sell int = 2 // 2卖单
|
||||
BrokerAge int = 3 // 手续费
|
||||
Transfer int = 4 // 划转(废弃)
|
||||
Profited int = 5 // 已实现盈亏
|
||||
LossFee int = 6 // 爆仓精算
|
||||
FundFee int = 7 // 资金费用
|
||||
InTransfer int = 13 // 内部转入
|
||||
OutTransfer int = 14 // 内部转出
|
||||
VtsToFut int = 101 // 现货划转合约
|
||||
FutToVts int = 102 // 合约转现货
|
||||
VtsToOtc int = 103 // 现货转法币
|
||||
OtcToVts int = 104 // 法币转现货
|
||||
FutCoinToVts int = 105 // 币本位划转现货
|
||||
VtsToFutCoin int = 106 // 现货划转到币本位
|
||||
)
|
||||
|
||||
var Types = map[int]string{
|
||||
Buy: "买单",
|
||||
Sell: "卖单",
|
||||
BrokerAge: "手续费",
|
||||
Transfer: "划转",
|
||||
Profited: "已实现盈亏",
|
||||
LossFee: "爆仓精算",
|
||||
FundFee: "资金费用",
|
||||
InTransfer: "内部转入",
|
||||
OutTransfer: "内部转出",
|
||||
VtsToFut: "现货划转合约",
|
||||
FutToVts: "合约转现货",
|
||||
VtsToOtc: "现货转法币",
|
||||
OtcToVts: "法币转现货",
|
||||
FutCoinToVts: "币本位划转现货",
|
||||
VtsToFutCoin: "现货划转到币本位",
|
||||
}
|
||||
7
common/const/enum/enable/enable.go
Normal file
7
common/const/enum/enable/enable.go
Normal file
@ -0,0 +1,7 @@
|
||||
package enable
|
||||
|
||||
// 是否启用(通用)
|
||||
const (
|
||||
Disable = 1 // 禁用
|
||||
Enable = 3 // 启用
|
||||
)
|
||||
6
common/const/enum/fut_coin_type/fut_coin_type.go
Normal file
6
common/const/enum/fut_coin_type/fut_coin_type.go
Normal file
@ -0,0 +1,6 @@
|
||||
package fut_coin_type
|
||||
|
||||
const (
|
||||
USDT = 1 // U本位
|
||||
COIN = 2 // 币本位
|
||||
)
|
||||
7
common/const/enum/grid_buytype/grid_buytype.go
Normal file
7
common/const/enum/grid_buytype/grid_buytype.go
Normal file
@ -0,0 +1,7 @@
|
||||
package grid_buytype
|
||||
|
||||
// 交易网格买卖类型
|
||||
const (
|
||||
Buy = 1 // 买
|
||||
Sell = 2 // 卖
|
||||
)
|
||||
7
common/const/enum/grid_createtype/grid_createtype.go
Normal file
7
common/const/enum/grid_createtype/grid_createtype.go
Normal file
@ -0,0 +1,7 @@
|
||||
package grid_createtype
|
||||
|
||||
// 交易网格创建方式
|
||||
const (
|
||||
OneCreation = 1 // 一键创建
|
||||
ManualCreation = 2 // 手动创建
|
||||
)
|
||||
8
common/const/enum/grid_dealtype/grid_dealtype.go
Normal file
8
common/const/enum/grid_dealtype/grid_dealtype.go
Normal file
@ -0,0 +1,8 @@
|
||||
package grid_dealtype
|
||||
|
||||
// 交易网格资金流水类型
|
||||
const (
|
||||
Occupy = 1 // 策略占用
|
||||
Release = 2 // 策略释放
|
||||
Profit = 3 // 策略利润
|
||||
)
|
||||
7
common/const/enum/grid_netmode/grid_netmode.go
Normal file
7
common/const/enum/grid_netmode/grid_netmode.go
Normal file
@ -0,0 +1,7 @@
|
||||
package grid_netmode
|
||||
|
||||
// 现货交易网格模式
|
||||
const (
|
||||
EqualRatio = 1 // 等比
|
||||
EqualDifference = 2 // 等差
|
||||
)
|
||||
@ -0,0 +1,9 @@
|
||||
package grid_spotorder_status
|
||||
|
||||
// 现货交易网格委托单状态
|
||||
const (
|
||||
UnDeal = 1 // 未成交
|
||||
Partial = 2 // 部分成交
|
||||
Complete = 3 // 完成成交
|
||||
Cancel = 4 // 取消
|
||||
)
|
||||
@ -0,0 +1,13 @@
|
||||
package grid_spotpolicy_state
|
||||
|
||||
// 现货交易网格状态
|
||||
const (
|
||||
Wait = 1 // 等待触发
|
||||
Starting = 2 // 正在启动
|
||||
Start = 3 // 启动完成
|
||||
Stopping = 4 // 正在停止
|
||||
ManualStop = 5 // 手动停止
|
||||
ProfitStop = 6 // 止盈停止
|
||||
LossStop = 7 // 止损停止
|
||||
SignalStop = 8 // 信号触发停止
|
||||
)
|
||||
7
common/const/enum/grid_triggertype/grid_triggertype.go
Normal file
7
common/const/enum/grid_triggertype/grid_triggertype.go
Normal file
@ -0,0 +1,7 @@
|
||||
package grid_triggertype
|
||||
|
||||
// 交易网格触发条件
|
||||
const (
|
||||
Immediately = 1 // 立即触发
|
||||
Price = 2 // 价格触发
|
||||
)
|
||||
@ -0,0 +1,9 @@
|
||||
package holddaylog_chart_daytype
|
||||
|
||||
// 1==24小时 3==7天 5==30天 7=3个月
|
||||
const (
|
||||
Hour_24 = 1 // 24小时
|
||||
Day_7 = 3 // 7天
|
||||
Day_30 = 5 // 30天
|
||||
Month_3 = 7 // 3个月
|
||||
)
|
||||
54
common/const/enum/kyctype/kyctype.go
Normal file
54
common/const/enum/kyctype/kyctype.go
Normal file
@ -0,0 +1,54 @@
|
||||
package kyctype
|
||||
|
||||
// IdCardLevel 身份等级
|
||||
type IdCardLevel int
|
||||
|
||||
const (
|
||||
// Junior 初级认证
|
||||
Junior IdCardLevel = 1
|
||||
|
||||
// Middle 中级认证
|
||||
Middle IdCardLevel = 2
|
||||
|
||||
// Senior 高级认证
|
||||
Senior IdCardLevel = 3
|
||||
)
|
||||
|
||||
// IdCardState 审核状态
|
||||
type IdCardState int
|
||||
|
||||
const (
|
||||
// UnAuth 待审核
|
||||
UnAuth IdCardState = 1
|
||||
|
||||
// AuthFailed 审核失败
|
||||
AuthFailed IdCardState = 2
|
||||
|
||||
// AuthSuccess 审核完成
|
||||
AuthSuccess IdCardState = 3
|
||||
)
|
||||
|
||||
// IdCardType 审核类型
|
||||
type IdCardType int
|
||||
|
||||
const (
|
||||
// IdCard 身份证
|
||||
IdCard IdCardType = 1
|
||||
|
||||
// Passport 护照
|
||||
Passport IdCardType = 2
|
||||
|
||||
// Drive 驾照
|
||||
Drive IdCardType = 3
|
||||
)
|
||||
|
||||
// VerifyType 审核类型
|
||||
type VerifyType int
|
||||
|
||||
const (
|
||||
// Third 第三方审核
|
||||
Third VerifyType = 1
|
||||
|
||||
// System 系统审核
|
||||
System VerifyType = 2
|
||||
)
|
||||
13
common/const/enum/language_map.go
Normal file
13
common/const/enum/language_map.go
Normal file
@ -0,0 +1,13 @@
|
||||
package enum
|
||||
|
||||
// 语言
|
||||
var LanguageMap = map[string]string{
|
||||
"en": "英语",
|
||||
"jp": "日本语",
|
||||
"kr": "韩语",
|
||||
"my": "马来西亚语",
|
||||
"th": "泰国语",
|
||||
"vn": "越南语",
|
||||
"zh-CN": "简体中文",
|
||||
"zh-HK": "繁体中文",
|
||||
}
|
||||
24
common/const/enum/logtype/logtype.go
Normal file
24
common/const/enum/logtype/logtype.go
Normal file
@ -0,0 +1,24 @@
|
||||
package logtype
|
||||
|
||||
// LogType 日志类型
|
||||
type LogType int
|
||||
|
||||
const (
|
||||
Min LogType = iota // 最小值
|
||||
Login // 登入
|
||||
Logout // 登出
|
||||
Create // 新增
|
||||
Update // 编辑
|
||||
Delete // 删除
|
||||
ChangePwd // 修改密码
|
||||
Max // 最大值
|
||||
)
|
||||
|
||||
var LogTypeMap = map[LogType]string{
|
||||
Login: "登入",
|
||||
Logout: "登出",
|
||||
Create: "新增",
|
||||
Update: "编辑",
|
||||
Delete: "删除",
|
||||
ChangePwd: "修改密码",
|
||||
}
|
||||
7
common/const/enum/longshorttype/longshorttype.go
Normal file
7
common/const/enum/longshorttype/longshorttype.go
Normal file
@ -0,0 +1,7 @@
|
||||
package longshorttype
|
||||
|
||||
// longshorttype
|
||||
const (
|
||||
Long = 1 // 做多
|
||||
Short = 2 // 做空
|
||||
)
|
||||
10
common/const/enum/ordertype/ordertype.go
Normal file
10
common/const/enum/ordertype/ordertype.go
Normal file
@ -0,0 +1,10 @@
|
||||
package ordertype
|
||||
|
||||
//订单类型:1限价,2限价止盈止损,3市价,4止盈止损市价单,5系统强平委托
|
||||
const (
|
||||
Limit = 1 //限价单,下单类型
|
||||
StopLimit = 2 //止盈止损限价单,下单类型
|
||||
Market = 3 //市价单,下单类型
|
||||
StopMarket = 4 //止盈止损市价单,下单类型
|
||||
Force = 5 //系统强平委托
|
||||
)
|
||||
@ -0,0 +1,8 @@
|
||||
package otc_advaduitstatus
|
||||
|
||||
// otc广告审核状态
|
||||
const (
|
||||
Pending = 1 // 待审核
|
||||
Pass = 3 // 审核通过
|
||||
Reject = 5 // 审核拒绝
|
||||
)
|
||||
7
common/const/enum/otc_advertisetype/otc_advertisetype.go
Normal file
7
common/const/enum/otc_advertisetype/otc_advertisetype.go
Normal file
@ -0,0 +1,7 @@
|
||||
package otc_advertisetype
|
||||
|
||||
// 广告类型
|
||||
const (
|
||||
Buy = 1 // 购买
|
||||
Sell = 3 // 出售
|
||||
)
|
||||
7
common/const/enum/otc_advstatus/otc_advstatus.go
Normal file
7
common/const/enum/otc_advstatus/otc_advstatus.go
Normal file
@ -0,0 +1,7 @@
|
||||
package otc_advstatus
|
||||
|
||||
// otc广告状态
|
||||
const (
|
||||
Offline = 1 // 下线
|
||||
Online = 3 // 上线
|
||||
)
|
||||
10
common/const/enum/otc_appealprogress/otc_appealprogress.go
Normal file
10
common/const/enum/otc_appealprogress/otc_appealprogress.go
Normal file
@ -0,0 +1,10 @@
|
||||
package otc_appealprogress
|
||||
|
||||
//申诉进程
|
||||
const (
|
||||
WaitDefendantProcess = 1 // 等待被诉方处理
|
||||
WaitAppealProcess = 3 // 等待申诉方处理
|
||||
Revoked = 5 // 已撤销
|
||||
WaitCustomer = 7 // 等待客服处理
|
||||
Arbitrated = 9 // 已仲裁
|
||||
)
|
||||
19
common/const/enum/otc_appealreason/otc_appealreason.go
Normal file
19
common/const/enum/otc_appealreason/otc_appealreason.go
Normal file
@ -0,0 +1,19 @@
|
||||
package otc_appealreason
|
||||
|
||||
// 申诉原因
|
||||
const (
|
||||
Paid = 1 // 我已付款,卖家未放行
|
||||
PayMore = 3 // 我向卖家多转了钱
|
||||
NotPay = 5 // 我没有收到买家付款
|
||||
MoneyErr = 7 // 买家付款金额不对
|
||||
Other = 9 // 其他
|
||||
|
||||
)
|
||||
|
||||
var Types = map[int]string{
|
||||
Paid: "我已付款,卖家未放行",
|
||||
PayMore: "我向卖家多转了钱",
|
||||
NotPay: "我没有收到买家付款",
|
||||
MoneyErr: "买家付款金额不对",
|
||||
Other: "其他",
|
||||
}
|
||||
8
common/const/enum/otc_appealstatus/otc_appealstatus.go
Normal file
8
common/const/enum/otc_appealstatus/otc_appealstatus.go
Normal file
@ -0,0 +1,8 @@
|
||||
package otc_appealstatus
|
||||
|
||||
// otc仲裁状态
|
||||
const (
|
||||
Not = 1 // 未仲裁
|
||||
BuyerWin = 3 // 买家胜,放行
|
||||
SellerWin = 5 // 卖家胜,取消订单
|
||||
)
|
||||
6
common/const/enum/otc_auditlevel/otc_auditlevel.go
Normal file
6
common/const/enum/otc_auditlevel/otc_auditlevel.go
Normal file
@ -0,0 +1,6 @@
|
||||
package otc_auditlevel
|
||||
|
||||
// 商家认证等级
|
||||
const (
|
||||
Certified = 3 // 认证商家
|
||||
)
|
||||
@ -0,0 +1,8 @@
|
||||
package otc_chatlogsourcetype
|
||||
|
||||
// 消息来源
|
||||
const (
|
||||
Android = 1
|
||||
Ios = 3
|
||||
Pc = 5
|
||||
)
|
||||
7
common/const/enum/otc_chatlogstatus/otc_chatlogstatus.go
Normal file
7
common/const/enum/otc_chatlogstatus/otc_chatlogstatus.go
Normal file
@ -0,0 +1,7 @@
|
||||
package otc_chatlogstatus
|
||||
|
||||
// 聊天发送状态
|
||||
const (
|
||||
Fail = 1 // 发送失败
|
||||
Success = 3 // 发送成功
|
||||
)
|
||||
8
common/const/enum/otc_chattype/otc_chattype.go
Normal file
8
common/const/enum/otc_chattype/otc_chattype.go
Normal file
@ -0,0 +1,8 @@
|
||||
package otc_chattype
|
||||
|
||||
// 通讯软件类型
|
||||
const (
|
||||
Telegram = 1
|
||||
Wechat = 3
|
||||
QQ = 5
|
||||
)
|
||||
7
common/const/enum/otc_complaint/otc_complaint.go
Normal file
7
common/const/enum/otc_complaint/otc_complaint.go
Normal file
@ -0,0 +1,7 @@
|
||||
package otc_complaint
|
||||
|
||||
// otc申诉方
|
||||
const (
|
||||
Buyer = 1 // 买方
|
||||
Seller = 3 // 卖方
|
||||
)
|
||||
8
common/const/enum/otc_consult/otc_consult.go
Normal file
8
common/const/enum/otc_consult/otc_consult.go
Normal file
@ -0,0 +1,8 @@
|
||||
package otc_consult
|
||||
|
||||
// otc协商结果
|
||||
const (
|
||||
Not = 1 // 未协商
|
||||
Unable = 3 // 无法协商
|
||||
Negotiated = 5 // 协商解决
|
||||
)
|
||||
20
common/const/enum/otc_holdlogdealtype/otc_holdlogdealtype.go
Normal file
20
common/const/enum/otc_holdlogdealtype/otc_holdlogdealtype.go
Normal file
@ -0,0 +1,20 @@
|
||||
package otc_holdlogdealtype
|
||||
|
||||
// otc 资金类型
|
||||
const (
|
||||
Buy = 1 // 买入
|
||||
Sell = 3 // 卖出
|
||||
Frozen = 7 // 冻结
|
||||
AppendBond = 9 // 追加保证金
|
||||
UnFreeze = 11 // 解冻
|
||||
Deduct = 13 // 扣除保证金
|
||||
VtsToOtc = 103 // 现货转法币
|
||||
OtcToVts = 104 // 法币转现货
|
||||
Buckle = 105 // 划扣
|
||||
SaleAdviserAdd = 201 // 出售广告添加
|
||||
SaleAdviserEdit = 203 // 出售广告编辑
|
||||
MerchantUnVerify = 205 // 商家解除认证 保证金将解除冻结
|
||||
MerchantAdd = 207 // 法币商家新增
|
||||
MerchantEdit = 209 // 法币商家编辑
|
||||
MerchantFrozenBond = 210 // 法币商家冻结保证金
|
||||
)
|
||||
7
common/const/enum/otc_holdstatus/otc_holdstatus.go
Normal file
7
common/const/enum/otc_holdstatus/otc_holdstatus.go
Normal file
@ -0,0 +1,7 @@
|
||||
package otc_holdstatus
|
||||
|
||||
// otc 账户状态
|
||||
const (
|
||||
Frozen = 1 // 冻结
|
||||
Normal = 3 // 正常
|
||||
)
|
||||
8
common/const/enum/otc_linktype/otc_linktype.go
Normal file
8
common/const/enum/otc_linktype/otc_linktype.go
Normal file
@ -0,0 +1,8 @@
|
||||
package otc_linktype
|
||||
|
||||
// 紧急联系人类型
|
||||
const (
|
||||
Family = 1 // 家人
|
||||
Friend = 3 // 朋友
|
||||
Colleague = 5 // 同事
|
||||
)
|
||||
@ -0,0 +1,7 @@
|
||||
package otc_merchantout_status
|
||||
|
||||
const (
|
||||
Wait = 1 // 待处理
|
||||
Passed = 3 // 已通过
|
||||
Rejected = 5 // 不通过
|
||||
)
|
||||
11
common/const/enum/otc_merchantstatus/otc_merchantstatus.go
Normal file
11
common/const/enum/otc_merchantstatus/otc_merchantstatus.go
Normal file
@ -0,0 +1,11 @@
|
||||
package otc_merchantstatus
|
||||
|
||||
// otc商家状态:1 申请中,3 不通过,5 正常,7 已禁用,9 已解除认证
|
||||
const (
|
||||
Applying = 1 // 申请中
|
||||
Fail = 3 // 不通过
|
||||
Normal = 5 // 正常
|
||||
Disabled = 7 // 已禁用
|
||||
Revoked = 9 // 已解除认证
|
||||
BondNoEnough = 11 // 保证金不足
|
||||
)
|
||||
7
common/const/enum/otc_msgtype/otc_msgtype.go
Normal file
7
common/const/enum/otc_msgtype/otc_msgtype.go
Normal file
@ -0,0 +1,7 @@
|
||||
package otc_msgtype
|
||||
|
||||
// otc 聊天消息类型
|
||||
const (
|
||||
Text = 1 // 文本
|
||||
Image = 3 // 图片
|
||||
)
|
||||
9
common/const/enum/otc_orderstatus/otc_orderstatus.go
Normal file
9
common/const/enum/otc_orderstatus/otc_orderstatus.go
Normal file
@ -0,0 +1,9 @@
|
||||
package otc_orderstatus
|
||||
|
||||
const (
|
||||
UnPay = 1 // 未付款
|
||||
WaitMoney = 3 // 待放币
|
||||
Complete = 5 // 已完成
|
||||
Complain = 7 // 申诉中
|
||||
Cancel = 9 // 已取消
|
||||
)
|
||||
7
common/const/enum/otc_ordertype/otc_ordertype.go
Normal file
7
common/const/enum/otc_ordertype/otc_ordertype.go
Normal file
@ -0,0 +1,7 @@
|
||||
package otc_ordertype
|
||||
|
||||
// 订单方向
|
||||
const (
|
||||
Buy = 1 // 买
|
||||
Sell = 3 // 卖
|
||||
)
|
||||
7
common/const/enum/otc_pricetype/otc_pricetype.go
Normal file
7
common/const/enum/otc_pricetype/otc_pricetype.go
Normal file
@ -0,0 +1,7 @@
|
||||
package otc_pricetype
|
||||
|
||||
//定价方式
|
||||
const (
|
||||
Fix = 1 // 固定价格
|
||||
Float = 3 // 浮动价格
|
||||
)
|
||||
10
common/const/enum/otc_progress/otc_progress.go
Normal file
10
common/const/enum/otc_progress/otc_progress.go
Normal file
@ -0,0 +1,10 @@
|
||||
package otc_progress
|
||||
|
||||
// otc申诉进程
|
||||
const (
|
||||
WaitSellerHandle = 1 // 等待卖家处理
|
||||
WaitBuyerHandle = 3 // 等待买家处理
|
||||
Revoked = 5 // 已撤销申诉
|
||||
WaitCustomer = 7 // 等待客服处理
|
||||
Arbitrated = 9 // 已仲裁
|
||||
)
|
||||
7
common/const/enum/otc_rateway/otc_rateway.go
Normal file
7
common/const/enum/otc_rateway/otc_rateway.go
Normal file
@ -0,0 +1,7 @@
|
||||
package otc_rateway
|
||||
|
||||
//浮动方式
|
||||
const (
|
||||
Up = 1 // 上浮
|
||||
Down = 3 // 下浮
|
||||
)
|
||||
7
common/const/enum/otc_tradetype/otc_tradetype.go
Normal file
7
common/const/enum/otc_tradetype/otc_tradetype.go
Normal file
@ -0,0 +1,7 @@
|
||||
package otc_tradetype
|
||||
|
||||
// 交易区
|
||||
const (
|
||||
Fast = 1 // 快捷区
|
||||
Self = 3 // 自选区
|
||||
)
|
||||
7
common/const/enum/showhide/showhide.go
Normal file
7
common/const/enum/showhide/showhide.go
Normal file
@ -0,0 +1,7 @@
|
||||
package showhide
|
||||
|
||||
// 是否隐藏账号(通用)
|
||||
const (
|
||||
Hide = 1 // 隐藏
|
||||
Show = 3 // 显示
|
||||
)
|
||||
25
common/const/enum/smstemplate/sms_template.go
Normal file
25
common/const/enum/smstemplate/sms_template.go
Normal file
@ -0,0 +1,25 @@
|
||||
package smstemplate
|
||||
|
||||
// 短信模板 Key 命名规则:SmsTemplate_BusinessType_Language
|
||||
const (
|
||||
// 通用业务模板 - 中文
|
||||
SmsTemplate_1_CN = "您正在进行【${business}】,验证码:${code},5分钟内有效,请勿泄露给任何人。如非本人操作请立即联系官方客服。"
|
||||
// 通用业务模板 - 英文
|
||||
SmsTemplate_1_EN = "Are you doing [${business}] Verification code: ${code}, valid within 5 minutes, please do not disclose it to anyone."
|
||||
// 卖家已确认收款_CN
|
||||
SmsTemplate_2_CN = "订单号 ${ordersn},卖家已确认收款,订单已完成,请前往账户查收资产。"
|
||||
// 卖家已确认收款_EN
|
||||
SmsTemplate_2_EN = "Order number ${ordersn}, the seller has confirmed the payment, the order has been completed, please go to the account to check the assets."
|
||||
// 卖家申诉_CN
|
||||
SmsTemplate_3_CN = "订单号 ${ordersn},卖家已发起申诉,请尽快前往处理该订单申诉。"
|
||||
// 卖家申诉_EN
|
||||
SmsTemplate_3_EN = "Order number ${ordersn}, the seller has initiated an appeal, please go to the appeal as soon as possible."
|
||||
// 买家已付款_CN
|
||||
SmsTemplate_4_CN = "订单号 ${ordersn},买家已付款,请查收款项进行放行。"
|
||||
// 买家已付款_EN
|
||||
SmsTemplate_4_EN = "Order number ${ordersn}, the buyer has paid, please check the payment for release."
|
||||
// 买家申诉_CN
|
||||
SmsTemplate_5_CN = "订单号 ${ordersn},买家已发起申诉,请尽快处理该订单申诉。"
|
||||
// 买家申诉_EN
|
||||
SmsTemplate_5_EN = "Order number ${ordersn}, the buyer has initiated an appeal, please handle the order appeal as soon as possible."
|
||||
)
|
||||
10
common/const/enum/transfertype/transfertype.go
Normal file
10
common/const/enum/transfertype/transfertype.go
Normal file
@ -0,0 +1,10 @@
|
||||
package transfertype
|
||||
|
||||
const (
|
||||
Vts2Fut = 1 // 现货划转合约
|
||||
Fut2Vts = 2 // 合约划转现货
|
||||
Vts2Otc = 3 // 现货划转Otc
|
||||
Otc2Vts = 4 // Otc划转现货
|
||||
FutCoin2Vts = 5 // 币本位划转现货
|
||||
Vts2FutCoin = 6 // 现货划转到币本位
|
||||
)
|
||||
7
common/const/enum/usefree/usefreee.go
Normal file
7
common/const/enum/usefree/usefreee.go
Normal file
@ -0,0 +1,7 @@
|
||||
package usefree
|
||||
|
||||
// usefree
|
||||
const (
|
||||
Available = 1 // 可用
|
||||
Frozen = 2 // 冻结
|
||||
)
|
||||
@ -0,0 +1,8 @@
|
||||
package vts_alertset_frequency
|
||||
|
||||
// frequency 提醒频率
|
||||
const (
|
||||
OnlyOnce = 1 // 仅提醒一次
|
||||
OnceDay = 2 // 每日提醒一次
|
||||
Always = 3 // 持续提醒(每当价格达到该值,则提醒一次)
|
||||
)
|
||||
@ -0,0 +1,7 @@
|
||||
package vts_alertset_state
|
||||
|
||||
// state 设置状态
|
||||
const (
|
||||
Normal = 1 // 正常
|
||||
Stop = 2 // 停止(比如频率是只提醒一次,提醒一次完就修改为2)
|
||||
)
|
||||
11
common/const/enum/vts_alertset_type/vts_alertset_type.go
Normal file
11
common/const/enum/vts_alertset_type/vts_alertset_type.go
Normal file
@ -0,0 +1,11 @@
|
||||
package vts_alertset_type
|
||||
|
||||
// alerttype 预警类型
|
||||
const (
|
||||
RisesAbove = 1 //1 价格涨到
|
||||
DropTo = 2 //2 价格跌到
|
||||
ChangeOver = 3 //3 涨幅达到
|
||||
ChangeUnder = 4 //4 跌幅达到
|
||||
H24ChangeOver = 5 //5 h24小时涨幅
|
||||
H24ChangeUnder = 6 //6 h24小时跌幅
|
||||
)
|
||||
43
common/const/enum/warehousetype/warehousetype.go
Normal file
43
common/const/enum/warehousetype/warehousetype.go
Normal file
@ -0,0 +1,43 @@
|
||||
package warehousetype
|
||||
|
||||
// WareHouse
|
||||
const (
|
||||
Isolated = 1 // 逐仓
|
||||
Cross = 2 // 全仓
|
||||
)
|
||||
|
||||
// DirectHouse 持仓模式
|
||||
const (
|
||||
OneWayMode = 1 // 单向持仓
|
||||
HedgeMode = 2 // 双向持仓
|
||||
)
|
||||
|
||||
// 下单持仓类型 1开多 2平多 3开空 4平空
|
||||
const (
|
||||
BuyLong = iota + 1 // 开多
|
||||
SellLog // 平多
|
||||
SellShort // 开空
|
||||
BuyShort // 平空
|
||||
)
|
||||
|
||||
const (
|
||||
BOTH = "both"
|
||||
LONG = "long"
|
||||
SHORT = "short"
|
||||
)
|
||||
|
||||
var (
|
||||
PosSide = []string{BOTH, LONG, SHORT} // 持仓方向集合
|
||||
)
|
||||
|
||||
const (
|
||||
Buy = 1 // 下单方向买
|
||||
Sell = 2 // 下单方向卖
|
||||
)
|
||||
const (
|
||||
PositionTypeOpen = 1 // 开仓
|
||||
PositionTypeCloseOpen = 2 // 反向开仓
|
||||
PositionTypeClose = 3 // 平仓
|
||||
PositionTypeForce = 4 // 系统强平
|
||||
PositionTypeSysClose = 5 // 系统平仓
|
||||
)
|
||||
7
common/const/enum/yesno/yesno.go
Normal file
7
common/const/enum/yesno/yesno.go
Normal file
@ -0,0 +1,7 @@
|
||||
package yesno
|
||||
|
||||
// 是否(通用)
|
||||
const (
|
||||
No = 1 // 否
|
||||
Yes = 3 // 是
|
||||
)
|
||||
62
common/const/rediskey/redis_key.go
Normal file
62
common/const/rediskey/redis_key.go
Normal file
@ -0,0 +1,62 @@
|
||||
package rediskey
|
||||
|
||||
const (
|
||||
IPPositionCache = "_IPPositionCache" // IP 归属地缓存
|
||||
AppLoginUserToken = "_AppLoginUserToken_%d" // App登录用户的Token {uid}
|
||||
AgentLoginUserToken = "_AgentLoginUserToken_%d" // PC端代理商登录用户的Token
|
||||
AgentEmailCode = "_AgentEmailCode_%d"
|
||||
AdminLoginUserToken = "_AdminLoginUserToken_%d" // 后台登录用户的Token {uid}
|
||||
PCLoginUserToken = "_PCLoginUserToken_%d" // PC端登录token
|
||||
UserLoginPwdErrFre = "_UserLoginPwdErrFre_%d" // 用户登录密码错误次数 {uid}
|
||||
UserCaptchaSendFre = "_UserCaptchaSendFre_%v_%d" // 用户验证码发送频次 {uid|ip}_{business}
|
||||
UserLoginWsClient = "_UserLoginWsClient" // websocket连接的客户端
|
||||
ScanLoginSecret = "_ScanLoginSecret_%v" // 扫码登录秘钥
|
||||
StatusCodeLanguage = "_StatusCodeLanguage_%v" // 状态码语言包_en
|
||||
PCRegisterEmail = "_PCRegister_%v" // 用户注册时邮箱key
|
||||
PCRegisterMobile = "_PCRegisterMobile_%v" // 用户注册时手机key
|
||||
SpotSymbolTicker = "_SpotSymbolTicker_" // 现货交易对行情
|
||||
FutSymbolTicker = "_FutSymbolTicker_" // 合约交易对行情
|
||||
PreOrderScriptList = "_ProOrderScriptList_" // 脚本执行list
|
||||
PreSpotOrderList = "_PreSpotOrderList_:%s" // 待触发的现货订单集合{交易所类型 exchange_type}
|
||||
PreFutOrderList = "_PreFutOrderList_:%s" // 待触发的订单集合 {交易所类型 exchange_type}
|
||||
|
||||
API_USER = "api_user:%v" // api用户
|
||||
SystemSetting = "system_setting" //系统设置
|
||||
ApiGroup = "api_group:%v" //api用户组 {id}
|
||||
ApiGroupAll = "api_group:"
|
||||
|
||||
ApiUserActiveList = "api_user_active_list" //已启用待连接websocket的api
|
||||
ApiUserDeleteList = "api_user_delete_list" //已删除待删除的api
|
||||
|
||||
FutStopTrigger = "fut_trigger_stop_lock:%v_%s" //合约止损触发锁
|
||||
SpotStopTrigger = "spot_trigger_stop_lock:%v_%s" //现货止损触发锁
|
||||
|
||||
SpotAddPositionTrigger = "spot_addposition_trigger:%v_%s" //现货加仓触发 {apiuserid|symbol}
|
||||
FutAddPositionTrigger = "fut_addposition_trigger:%v_%s" //合约加仓触发 {apiuserid|symbol}
|
||||
SpotTrigger = "spot_trigger_lock:%v_%s" //现货触发 {apiuserid|symbol}
|
||||
FutTrigger = "fut_trigger_lock:%v_%s" //合约触发 {apiuserid|symbol}
|
||||
|
||||
SpotCallBack = "spot_callback:%s" //现货回调 {ordersn}
|
||||
FutCallBack = "fut_callback:%s" //合约回调 {ordersn}
|
||||
|
||||
//需要清理键值---------BEGIN---------------
|
||||
|
||||
UserHolding = "api_user_hold:%v" //用户持仓币种 {主单apiid} 不是交易对
|
||||
|
||||
SpotHedgeClosePosition = "spot_hedge_close_position:%v_%s" //现货对冲平仓 {mainorderid|symbol}
|
||||
FuturesHedgeClosePosition = "futures_hedge_close_position:%v_%s" //合约对冲平仓 {mainorderid|symbol}
|
||||
|
||||
SpotStopLossList = "spot_stoploss_list" //现货止损待触发列表
|
||||
FuturesStopLossList = "futures_stoploss_list" //合约止损待触发列表
|
||||
|
||||
SpotAddPositionList = "spot_add_position_list" //现货加仓待触发
|
||||
FuturesAddPositionList = "futures_add_position_list" //合约加仓待触发
|
||||
StoplossMarkt = "stop_loss_markt" //对冲保险单(市价) 对冲单成交之后、清除
|
||||
|
||||
HoldeA = "holde_a:%v" //持仓A {主单id}
|
||||
HoldeB = "holde_b:%v" //持仓B {主单id}
|
||||
|
||||
HedgeClosePosition = "hedge_close_position:%v" //对冲平仓记录 {主单id}
|
||||
//需要清理键值---------END-----------------
|
||||
|
||||
)
|
||||
28
common/const/userlockkey/userlockkey.go
Normal file
28
common/const/userlockkey/userlockkey.go
Normal file
@ -0,0 +1,28 @@
|
||||
package userlockkey
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
preOrder = "Order-" //分布式锁现货委托单+划转前缀
|
||||
|
||||
preFutOrder = "futOrder-" //分布式锁u本位--合约委托单+划转前缀
|
||||
|
||||
preFutCoinOrder = "futCoinOrder-" //分布式锁币本位--合约委托单+划转前缀
|
||||
)
|
||||
|
||||
// GetUserLockKey 分布式锁现货 用户 key
|
||||
func GetUserLockKey(userId int) string {
|
||||
return preOrder + strconv.Itoa(userId)
|
||||
}
|
||||
|
||||
// GetUserFutLockKey u本位--分布式锁合约 用户 key
|
||||
func GetUserFutLockKey(userId int) string {
|
||||
return preFutOrder + strconv.Itoa(userId)
|
||||
}
|
||||
|
||||
// GetUserFutCoinLockKey 币本位--分布式锁合约 用户 key
|
||||
func GetUserFutCoinLockKey(userId int) string {
|
||||
return preFutCoinOrder + strconv.Itoa(userId)
|
||||
}
|
||||
65
common/database/initialize.go
Normal file
65
common/database/initialize.go
Normal file
@ -0,0 +1,65 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
toolsConfig "github.com/go-admin-team/go-admin-core/sdk/config"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
mycasbin "github.com/go-admin-team/go-admin-core/sdk/pkg/casbin"
|
||||
toolsDB "github.com/go-admin-team/go-admin-core/tools/database"
|
||||
. "github.com/go-admin-team/go-admin-core/tools/gorm/logger"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"go-admin/common/global"
|
||||
)
|
||||
|
||||
// Setup 配置数据库
|
||||
func Setup() {
|
||||
for k := range toolsConfig.DatabasesConfig {
|
||||
setupSimpleDatabase(k, toolsConfig.DatabasesConfig[k])
|
||||
}
|
||||
}
|
||||
|
||||
func setupSimpleDatabase(host string, c *toolsConfig.Database) {
|
||||
if global.Driver == "" {
|
||||
global.Driver = c.Driver
|
||||
}
|
||||
log.Infof("%s => %s", host, pkg.Green(c.Source))
|
||||
registers := make([]toolsDB.ResolverConfigure, len(c.Registers))
|
||||
for i := range c.Registers {
|
||||
registers[i] = toolsDB.NewResolverConfigure(
|
||||
c.Registers[i].Sources,
|
||||
c.Registers[i].Replicas,
|
||||
c.Registers[i].Policy,
|
||||
c.Registers[i].Tables)
|
||||
}
|
||||
resolverConfig := toolsDB.NewConfigure(c.Source, c.MaxIdleConns, c.MaxOpenConns, c.ConnMaxIdleTime, c.ConnMaxLifeTime, registers)
|
||||
db, err := resolverConfig.Init(&gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
SingularTable: true,
|
||||
},
|
||||
Logger: New(
|
||||
logger.Config{
|
||||
SlowThreshold: time.Second,
|
||||
Colorful: true,
|
||||
LogLevel: logger.LogLevel(
|
||||
log.DefaultLogger.Options().Level.LevelForGorm()),
|
||||
},
|
||||
),
|
||||
}, opens[c.Driver])
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(pkg.Red(c.Driver+" connect error :"), err)
|
||||
} else {
|
||||
log.Info(pkg.Green(c.Driver + " connect success !"))
|
||||
}
|
||||
|
||||
e := mycasbin.Setup(db, "")
|
||||
|
||||
sdk.Runtime.SetDb(host, db)
|
||||
sdk.Runtime.SetCasbin(host, e)
|
||||
}
|
||||
16
common/database/open.go
Normal file
16
common/database/open.go
Normal file
@ -0,0 +1,16 @@
|
||||
//go:build !sqlite3
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlserver"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var opens = map[string]func(string) gorm.Dialector{
|
||||
"mysql": mysql.Open,
|
||||
"postgres": postgres.Open,
|
||||
"sqlserver": sqlserver.Open,
|
||||
}
|
||||
19
common/database/open_sqlite3.go
Normal file
19
common/database/open_sqlite3.go
Normal file
@ -0,0 +1,19 @@
|
||||
//go:build sqlite3
|
||||
// +build sqlite3
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/driver/sqlserver"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var opens = map[string]func(string) gorm.Dialector{
|
||||
"mysql": mysql.Open,
|
||||
"postgres": postgres.Open,
|
||||
"sqlite3": sqlite.Open,
|
||||
"sqlserver": sqlserver.Open,
|
||||
}
|
||||
8
common/dto/api_group.go
Normal file
8
common/dto/api_group.go
Normal file
@ -0,0 +1,8 @@
|
||||
package dto
|
||||
|
||||
type ApiGroupDto struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ApiUserId int `json:"apiUserId"`
|
||||
ChildApiUserId int `json:"childApiUserId"`
|
||||
}
|
||||
74
common/dto/auto_form.go
Normal file
74
common/dto/auto_form.go
Normal file
@ -0,0 +1,74 @@
|
||||
package dto
|
||||
|
||||
type AutoForm struct {
|
||||
Fields []Field `json:"fields"`
|
||||
FormRef string `json:"formRef"`
|
||||
FormModel string `json:"formModel"`
|
||||
Size string `json:"size"`
|
||||
LabelPosition string `json:"labelPosition"`
|
||||
LabelWidth int `json:"labelWidth"`
|
||||
FormRules string `json:"formRules"`
|
||||
Gutter int `json:"gutter"`
|
||||
Disabled bool `json:"disabled"`
|
||||
Span int `json:"span"`
|
||||
FormBtns bool `json:"formBtns"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Label string `json:"label"`
|
||||
LabelWidth interface{} `json:"labelWidth"`
|
||||
ShowLabel bool `json:"showLabel"`
|
||||
ChangeTag bool `json:"changeTag"`
|
||||
Tag string `json:"tag"`
|
||||
TagIcon string `json:"tagIcon"`
|
||||
Required bool `json:"required"`
|
||||
Layout string `json:"layout"`
|
||||
Span int `json:"span"`
|
||||
Document string `json:"document"`
|
||||
RegList []interface{} `json:"regList"`
|
||||
FormId int `json:"formId"`
|
||||
RenderKey int64 `json:"renderKey"`
|
||||
DefaultValue interface{} `json:"defaultValue"`
|
||||
ShowTip bool `json:"showTip,omitempty"`
|
||||
ButtonText string `json:"buttonText,omitempty"`
|
||||
FileSize int `json:"fileSize,omitempty"`
|
||||
SizeUnit string `json:"sizeUnit,omitempty"`
|
||||
}
|
||||
|
||||
type Option struct {
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type Slot struct {
|
||||
Prepend string `json:"prepend,omitempty"`
|
||||
Append string `json:"append,omitempty"`
|
||||
ListType bool `json:"list-type,omitempty"`
|
||||
Options []Option `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Config Config `json:"__config__"`
|
||||
Slot Slot `json:"__slot__"`
|
||||
Placeholder string `json:"placeholder,omitempty"`
|
||||
Style Style `json:"style,omitempty"`
|
||||
Clearable bool `json:"clearable,omitempty"`
|
||||
PrefixIcon string `json:"prefix-icon,omitempty"`
|
||||
SuffixIcon string `json:"suffix-icon,omitempty"`
|
||||
Maxlength interface{} `json:"maxlength"`
|
||||
ShowWordLimit bool `json:"show-word-limit,omitempty"`
|
||||
Readonly bool `json:"readonly,omitempty"`
|
||||
Disabled bool `json:"disabled"`
|
||||
VModel string `json:"__vModel__"`
|
||||
Action string `json:"action,omitempty"`
|
||||
Accept string `json:"accept,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
AutoUpload bool `json:"auto-upload,omitempty"`
|
||||
ListType string `json:"list-type,omitempty"`
|
||||
Multiple bool `json:"multiple,omitempty"`
|
||||
Filterable bool `json:"filterable,omitempty"`
|
||||
}
|
||||
|
||||
type Style struct {
|
||||
Width string `json:"width"`
|
||||
}
|
||||
106
common/dto/generate.go
Normal file
106
common/dto/generate.go
Normal file
@ -0,0 +1,106 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
vd "github.com/bytedance/go-tagexpr/v2/validator"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
)
|
||||
|
||||
type ObjectById struct {
|
||||
Id int `uri:"id"`
|
||||
Ids []int `json:"ids"`
|
||||
}
|
||||
|
||||
func (s *ObjectById) Bind(ctx *gin.Context) error {
|
||||
var err error
|
||||
log := api.GetRequestLogger(ctx)
|
||||
err = ctx.ShouldBindUri(s)
|
||||
if err != nil {
|
||||
log.Warnf("ShouldBindUri error: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
if ctx.Request.Method == http.MethodDelete {
|
||||
err = ctx.ShouldBind(&s)
|
||||
if err != nil {
|
||||
log.Warnf("ShouldBind error: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
if len(s.Ids) > 0 {
|
||||
return nil
|
||||
}
|
||||
if s.Ids == nil {
|
||||
s.Ids = make([]int, 0)
|
||||
}
|
||||
if s.Id != 0 {
|
||||
s.Ids = append(s.Ids, s.Id)
|
||||
}
|
||||
}
|
||||
if err = vd.Validate(s); err != nil {
|
||||
log.Errorf("Validate error: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *ObjectById) GetId() interface{} {
|
||||
if len(s.Ids) > 0 {
|
||||
s.Ids = append(s.Ids, s.Id)
|
||||
return s.Ids
|
||||
}
|
||||
return s.Id
|
||||
}
|
||||
|
||||
type ObjectGetReq struct {
|
||||
Id int `uri:"id"`
|
||||
}
|
||||
|
||||
func (s *ObjectGetReq) Bind(ctx *gin.Context) error {
|
||||
var err error
|
||||
log := api.GetRequestLogger(ctx)
|
||||
err = ctx.ShouldBindUri(s)
|
||||
if err != nil {
|
||||
log.Warnf("ShouldBindUri error: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
if err = vd.Validate(s); err != nil {
|
||||
log.Errorf("Validate error: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *ObjectGetReq) GetId() interface{} {
|
||||
return s.Id
|
||||
}
|
||||
|
||||
type ObjectDeleteReq struct {
|
||||
Ids []int `json:"ids"`
|
||||
}
|
||||
|
||||
func (s *ObjectDeleteReq) Bind(ctx *gin.Context) error {
|
||||
var err error
|
||||
log := api.GetRequestLogger(ctx)
|
||||
err = ctx.ShouldBind(&s)
|
||||
if err != nil {
|
||||
log.Warnf("ShouldBind error: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
if len(s.Ids) > 0 {
|
||||
return nil
|
||||
}
|
||||
if s.Ids == nil {
|
||||
s.Ids = make([]int, 0)
|
||||
}
|
||||
|
||||
if err = vd.Validate(s); err != nil {
|
||||
log.Errorf("Validate error: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *ObjectDeleteReq) GetId() interface{} {
|
||||
return s.Ids
|
||||
}
|
||||
12
common/dto/order.go
Normal file
12
common/dto/order.go
Normal file
@ -0,0 +1,12 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
func OrderDest(sort string, bl bool) func(db *gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Order(clause.OrderByColumn{Column: clause.Column{Name: sort}, Desc: bl})
|
||||
}
|
||||
}
|
||||
20
common/dto/pagination.go
Normal file
20
common/dto/pagination.go
Normal file
@ -0,0 +1,20 @@
|
||||
package dto
|
||||
|
||||
type Pagination struct {
|
||||
PageIndex int `form:"pageIndex"`
|
||||
PageSize int `form:"pageSize"`
|
||||
}
|
||||
|
||||
func (m *Pagination) GetPageIndex() int {
|
||||
if m.PageIndex <= 0 {
|
||||
m.PageIndex = 1
|
||||
}
|
||||
return m.PageIndex
|
||||
}
|
||||
|
||||
func (m *Pagination) GetPageSize() int {
|
||||
if m.PageSize <= 0 {
|
||||
m.PageSize = 10
|
||||
}
|
||||
return m.PageSize
|
||||
}
|
||||
84
common/dto/search.go
Normal file
84
common/dto/search.go
Normal file
@ -0,0 +1,84 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"github.com/go-admin-team/go-admin-core/tools/search"
|
||||
"go-admin/common/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type GeneralDelDto struct {
|
||||
Id int `uri:"id" json:"id" validate:"required"`
|
||||
Ids []int `json:"ids"`
|
||||
}
|
||||
|
||||
func (g GeneralDelDto) GetIds() []int {
|
||||
ids := make([]int, 0)
|
||||
if g.Id != 0 {
|
||||
ids = append(ids, g.Id)
|
||||
}
|
||||
if len(g.Ids) > 0 {
|
||||
for _, id := range g.Ids {
|
||||
if id > 0 {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if g.Id > 0 {
|
||||
ids = append(ids, g.Id)
|
||||
}
|
||||
}
|
||||
if len(ids) <= 0 {
|
||||
//方式全部删除
|
||||
ids = append(ids, 0)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
type GeneralGetDto struct {
|
||||
Id int `uri:"id" json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
func MakeCondition(q interface{}) func(db *gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
condition := &search.GormCondition{
|
||||
GormPublic: search.GormPublic{},
|
||||
Join: make([]*search.GormJoin, 0),
|
||||
}
|
||||
search.ResolveSearchQuery(global.Driver, q, condition)
|
||||
for _, join := range condition.Join {
|
||||
if join == nil {
|
||||
continue
|
||||
}
|
||||
db = db.Joins(join.JoinOn)
|
||||
for k, v := range join.Where {
|
||||
db = db.Where(k, v...)
|
||||
}
|
||||
for k, v := range join.Or {
|
||||
db = db.Or(k, v...)
|
||||
}
|
||||
for _, o := range join.Order {
|
||||
db = db.Order(o)
|
||||
}
|
||||
}
|
||||
for k, v := range condition.Where {
|
||||
db = db.Where(k, v...)
|
||||
}
|
||||
for k, v := range condition.Or {
|
||||
db = db.Or(k, v...)
|
||||
}
|
||||
for _, o := range condition.Order {
|
||||
db = db.Order(o)
|
||||
}
|
||||
return db
|
||||
}
|
||||
}
|
||||
|
||||
func Paginate(pageSize, pageIndex int) func(db *gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
offset := (pageIndex - 1) * pageSize
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
return db.Offset(offset).Limit(pageSize)
|
||||
}
|
||||
}
|
||||
21
common/dto/type.go
Normal file
21
common/dto/type.go
Normal file
@ -0,0 +1,21 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"go-admin/common/models"
|
||||
)
|
||||
|
||||
type Index interface {
|
||||
Generate() Index
|
||||
Bind(ctx *gin.Context) error
|
||||
GetPageIndex() int
|
||||
GetPageSize() int
|
||||
GetNeedSearch() interface{}
|
||||
}
|
||||
|
||||
type Control interface {
|
||||
Generate() Control
|
||||
Bind(ctx *gin.Context) error
|
||||
GenerateM() (models.ActiveRecord, error)
|
||||
GetId() interface{}
|
||||
}
|
||||
45
common/file_store/initialize.go
Normal file
45
common/file_store/initialize.go
Normal file
@ -0,0 +1,45 @@
|
||||
package file_store
|
||||
|
||||
import "fmt"
|
||||
|
||||
type OXS struct {
|
||||
// Endpoint 访问域名
|
||||
Endpoint string
|
||||
// AccessKeyID AK
|
||||
AccessKeyID string
|
||||
// AccessKeySecret AKS
|
||||
AccessKeySecret string
|
||||
// BucketName 桶名称
|
||||
BucketName string
|
||||
}
|
||||
|
||||
// Setup 配置文件存储driver
|
||||
func (e *OXS) Setup(driver DriverType, options ...ClientOption) FileStoreType {
|
||||
fileStoreType := driver
|
||||
var fileStore FileStoreType
|
||||
switch fileStoreType {
|
||||
case AliYunOSS:
|
||||
fileStore = new(ALiYunOSS)
|
||||
err := fileStore.Setup(e.Endpoint, e.AccessKeyID, e.AccessKeySecret, e.BucketName)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return fileStore
|
||||
case HuaweiOBS:
|
||||
fileStore = new(HuaWeiOBS)
|
||||
err := fileStore.Setup(e.Endpoint, e.AccessKeyID, e.AccessKeySecret, e.BucketName)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return fileStore
|
||||
case QiNiuKodo:
|
||||
fileStore = new(QiNiuKODO)
|
||||
err := fileStore.Setup(e.Endpoint, e.AccessKeyID, e.AccessKeySecret, e.BucketName)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return fileStore
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
27
common/file_store/interface.go
Normal file
27
common/file_store/interface.go
Normal file
@ -0,0 +1,27 @@
|
||||
package file_store
|
||||
|
||||
// DriverType 驱动类型
|
||||
type DriverType string
|
||||
|
||||
const (
|
||||
// HuaweiOBS 华为云OBS
|
||||
HuaweiOBS DriverType = "HuaweiOBS"
|
||||
// AliYunOSS 阿里云OSS
|
||||
AliYunOSS DriverType = "AliYunOSS"
|
||||
// QiNiuKodo 七牛云kodo
|
||||
QiNiuKodo DriverType = "QiNiuKodo"
|
||||
)
|
||||
|
||||
type ClientOption map[string]interface{}
|
||||
|
||||
// TODO: FileStoreType名称待定
|
||||
|
||||
// FileStoreType OXS
|
||||
type FileStoreType interface {
|
||||
// Setup 装载 endpoint sss
|
||||
Setup(endpoint, accessKeyID, accessKeySecret, BucketName string, options ...ClientOption) error
|
||||
// UpLoad 上传
|
||||
UpLoad(yourObjectName string, localFile interface{}) error
|
||||
// GetTempToken 获取临时Token
|
||||
GetTempToken() (string, error)
|
||||
}
|
||||
111
common/file_store/kodo.go
Normal file
111
common/file_store/kodo.go
Normal file
@ -0,0 +1,111 @@
|
||||
package file_store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/qiniu/go-sdk/v7/auth/qbox"
|
||||
"github.com/qiniu/go-sdk/v7/storage"
|
||||
)
|
||||
|
||||
type Zone string
|
||||
|
||||
const (
|
||||
// HuaDong 华东
|
||||
HuaDong Zone = "HuaDong"
|
||||
// HuaBei 华北
|
||||
HuaBei Zone = "HuaBei"
|
||||
// HuaNan 华南
|
||||
HuaNan Zone = "HuaNan"
|
||||
// BeiMei 北美
|
||||
BeiMei Zone = "BeiMei"
|
||||
// XinJiaPo 新加坡
|
||||
XinJiaPo Zone = "XinJiaPo"
|
||||
)
|
||||
|
||||
type QiNiuKODO struct {
|
||||
Client interface{}
|
||||
BucketName string
|
||||
cfg storage.Config
|
||||
options []ClientOption
|
||||
}
|
||||
|
||||
func (e *QiNiuKODO) getToken() string {
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: e.BucketName,
|
||||
}
|
||||
if len(e.options) > 0 && e.options[0]["Expires"] != nil {
|
||||
putPolicy.Expires = e.options[0]["Expires"].(uint64)
|
||||
}
|
||||
upToken := putPolicy.UploadToken(e.Client.(*qbox.Mac))
|
||||
return upToken
|
||||
}
|
||||
|
||||
//Setup 装载
|
||||
//endpoint sss
|
||||
func (e *QiNiuKODO) Setup(endpoint, accessKeyID, accessKeySecret, BucketName string, options ...ClientOption) error {
|
||||
|
||||
mac := qbox.NewMac(accessKeyID, accessKeySecret)
|
||||
// 获取存储空间。
|
||||
cfg := storage.Config{}
|
||||
// 空间对应的机房
|
||||
e.setZoneORDefault(cfg, options...)
|
||||
// 是否使用https域名
|
||||
cfg.UseHTTPS = true
|
||||
// 上传是否使用CDN上传加速
|
||||
cfg.UseCdnDomains = false
|
||||
|
||||
e.Client = mac
|
||||
e.BucketName = BucketName
|
||||
e.cfg = cfg
|
||||
e.options = options
|
||||
return nil
|
||||
}
|
||||
|
||||
// setZoneORDefault 设置Zone或者默认华东
|
||||
func (e *QiNiuKODO) setZoneORDefault(cfg storage.Config, options ...ClientOption) {
|
||||
if len(options) > 0 && options[0]["Zone"] != nil {
|
||||
if _, ok := options[0]["Zone"].(Zone); !ok {
|
||||
cfg.Zone = &storage.ZoneHuadong
|
||||
}
|
||||
switch options[0]["Zone"].(Zone) {
|
||||
case HuaDong:
|
||||
cfg.Zone = &storage.ZoneHuadong
|
||||
case HuaBei:
|
||||
cfg.Zone = &storage.ZoneHuabei
|
||||
case HuaNan:
|
||||
cfg.Zone = &storage.ZoneHuanan
|
||||
case BeiMei:
|
||||
cfg.Zone = &storage.ZoneBeimei
|
||||
case XinJiaPo:
|
||||
cfg.Zone = &storage.ZoneXinjiapo
|
||||
default:
|
||||
cfg.Zone = &storage.ZoneHuadong
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpLoad 文件上传
|
||||
func (e *QiNiuKODO) UpLoad(yourObjectName string, localFile interface{}) error {
|
||||
|
||||
// 构建表单上传的对象
|
||||
formUploader := storage.NewFormUploader(&e.cfg)
|
||||
ret := storage.PutRet{}
|
||||
// 可选配置
|
||||
putExtra := storage.PutExtra{
|
||||
Params: map[string]string{
|
||||
"x:name": "github logo",
|
||||
},
|
||||
}
|
||||
err := formUploader.PutFile(context.Background(), &ret, e.getToken(), yourObjectName, localFile.(string), &putExtra)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
fmt.Println(ret.Key, ret.Hash)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *QiNiuKODO) GetTempToken() (string, error) {
|
||||
token := e.getToken()
|
||||
return token, nil
|
||||
}
|
||||
23
common/file_store/kodo_test.go
Normal file
23
common/file_store/kodo_test.go
Normal file
@ -0,0 +1,23 @@
|
||||
package file_store
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKODOUpload(t *testing.T) {
|
||||
e := OXS{"", "", "", ""}
|
||||
var oxs = e.Setup(QiNiuKodo, map[string]interface{}{"Zone": "华东"})
|
||||
err := oxs.UpLoad("test.png", "./test.png")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
|
||||
func TestKODOGetTempToken(t *testing.T) {
|
||||
e := OXS{"", "", "", ""}
|
||||
var oxs = e.Setup(QiNiuKodo, map[string]interface{}{"Zone": "华东"})
|
||||
token, _ := oxs.GetTempToken()
|
||||
t.Log(token)
|
||||
t.Log("ok")
|
||||
}
|
||||
52
common/file_store/obs.go
Normal file
52
common/file_store/obs.go
Normal file
@ -0,0 +1,52 @@
|
||||
package file_store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/huaweicloud/huaweicloud-sdk-go-obs/obs"
|
||||
"log"
|
||||
)
|
||||
|
||||
type HuaWeiOBS struct {
|
||||
Client interface{}
|
||||
BucketName string
|
||||
}
|
||||
|
||||
func (e *HuaWeiOBS) Setup(endpoint, accessKeyID, accessKeySecret, BucketName string, options ...ClientOption) error {
|
||||
// 创建ObsClient结构体
|
||||
client, err := obs.New(accessKeyID, accessKeySecret, endpoint)
|
||||
if err != nil {
|
||||
log.Println("Error:", err)
|
||||
return err
|
||||
}
|
||||
e.Client = client
|
||||
e.BucketName = BucketName
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpLoad 文件上传
|
||||
// yourObjectName 文件路径名称,与objectKey是同一概念,表示断点续传上传文件到OSS时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg
|
||||
func (e *HuaWeiOBS) UpLoad(yourObjectName string, localFile interface{}) error {
|
||||
// 获取存储空间。
|
||||
input := &obs.PutFileInput{}
|
||||
input.Bucket = e.BucketName
|
||||
input.Key = yourObjectName
|
||||
input.SourceFile = localFile.(string)
|
||||
output, err := e.Client.(*obs.ObsClient).PutFile(input)
|
||||
|
||||
if err == nil {
|
||||
fmt.Printf("RequestId:%s\n", output.RequestId)
|
||||
fmt.Printf("ETag:%s, StorageClass:%s\n", output.ETag, output.StorageClass)
|
||||
} else {
|
||||
if obsError, ok := err.(obs.ObsError); ok {
|
||||
fmt.Println(obsError.Code)
|
||||
fmt.Println(obsError.Message)
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *HuaWeiOBS) GetTempToken() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
15
common/file_store/obs_test.go
Normal file
15
common/file_store/obs_test.go
Normal file
@ -0,0 +1,15 @@
|
||||
package file_store
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOBSUpload(t *testing.T) {
|
||||
e := OXS{"", "", "", ""}
|
||||
var oxs = e.Setup(HuaweiOBS)
|
||||
err := oxs.UpLoad("test.png", "./test.png")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
48
common/file_store/oss.go
Normal file
48
common/file_store/oss.go
Normal file
@ -0,0 +1,48 @@
|
||||
package file_store
|
||||
|
||||
import (
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"log"
|
||||
)
|
||||
|
||||
type ALiYunOSS struct {
|
||||
Client interface{}
|
||||
BucketName string
|
||||
}
|
||||
|
||||
//Setup 装载
|
||||
//endpoint sss
|
||||
func (e *ALiYunOSS) Setup(endpoint, accessKeyID, accessKeySecret, BucketName string, options ...ClientOption) error {
|
||||
client, err := oss.New(endpoint, accessKeyID, accessKeySecret)
|
||||
if err != nil {
|
||||
log.Println("Error:", err)
|
||||
return err
|
||||
}
|
||||
e.Client = client
|
||||
e.BucketName = BucketName
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpLoad 文件上传
|
||||
func (e *ALiYunOSS) UpLoad(yourObjectName string, localFile interface{}) error {
|
||||
// 获取存储空间。
|
||||
bucket, err := e.Client.(*oss.Client).Bucket(e.BucketName)
|
||||
if err != nil {
|
||||
log.Println("Error:", err)
|
||||
return err
|
||||
}
|
||||
// 设置分片大小为100 KB,指定分片上传并发数为3,并开启断点续传上传。
|
||||
// 其中<yourObjectName>与objectKey是同一概念,表示断点续传上传文件到OSS时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
|
||||
// "LocalFile"为filePath,100*1024为partSize。
|
||||
err = bucket.UploadFile(yourObjectName, localFile.(string), 100*1024, oss.Routines(3), oss.Checkpoint(true, ""))
|
||||
if err != nil {
|
||||
log.Println("Error:", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ALiYunOSS) GetTempToken() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
16
common/file_store/oss_test.go
Normal file
16
common/file_store/oss_test.go
Normal file
@ -0,0 +1,16 @@
|
||||
package file_store
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOSSUpload(t *testing.T) {
|
||||
// 打括号内填写自己的测试信息即可
|
||||
e := OXS{}
|
||||
var oxs = e.Setup(AliYunOSS)
|
||||
err := oxs.UpLoad("test.png", "./test.png")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
20
common/global/adm.go
Normal file
20
common/global/adm.go
Normal file
@ -0,0 +1,20 @@
|
||||
package global
|
||||
|
||||
const (
|
||||
// Version go-admin version info
|
||||
Version = "2.1.2"
|
||||
)
|
||||
|
||||
var (
|
||||
// Driver 数据库驱动
|
||||
Driver string
|
||||
)
|
||||
|
||||
const (
|
||||
//钱包 回调配置
|
||||
SYS_CONFIG_CALLBACK = "CoinGateCallBack"
|
||||
//钱包 取消配置
|
||||
SYS_CONFIG_CANCECL = "CoinGateCancel"
|
||||
////钱包 成功配置
|
||||
SYS_CONFIG_SUCCESS = "CoinGateSuccess"
|
||||
)
|
||||
18
common/global/casbin.go
Normal file
18
common/global/casbin.go
Normal file
@ -0,0 +1,18 @@
|
||||
package global
|
||||
|
||||
import (
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
)
|
||||
|
||||
func LoadPolicy(c *gin.Context) (*casbin.SyncedEnforcer, error) {
|
||||
log := api.GetRequestLogger(c)
|
||||
if err := sdk.Runtime.GetCasbinKey(c.Request.Host).LoadPolicy(); err == nil {
|
||||
return sdk.Runtime.GetCasbinKey(c.Request.Host), err
|
||||
} else {
|
||||
log.Errorf("casbin rbac_model or policy init error, %s ", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
25
common/global/coingate.go
Normal file
25
common/global/coingate.go
Normal file
@ -0,0 +1,25 @@
|
||||
package global
|
||||
|
||||
const (
|
||||
//内部生成默认状态
|
||||
COINGATE_STATUS_DEFAULT = "default"
|
||||
|
||||
//新创建的发票。购物者尚未选择付款货币。
|
||||
COINGATE_STATUS_NEW = "new"
|
||||
// 购物者已选择支付货币。正在等待付款
|
||||
COINGATE_STATUS_PENDING = "pending"
|
||||
//购物者已转账支付发票款项。正在等待区块链网络确认。
|
||||
COINGATE_STATUS_CONFIRMING = "confirming"
|
||||
//付款已由网络确认,并记入商家账户。购买的商品/服务可以安全地交付给购物者。
|
||||
COINGATE_STATUS_PAID = "paid"
|
||||
//由于 AML/CTF 合规原因,付款被网络拒绝或被标记为无效
|
||||
COINGATE_STATUS_INVALID = "invalid"
|
||||
//购物者未在规定时间内付款(默认值:20 分钟),因此发票已过期。
|
||||
COINGATE_STATUS_EXPIRED = "expired"
|
||||
//购物者取消了发票。
|
||||
COINGATE_STATUS_CANCELED = "canceled"
|
||||
//付款已退还给购物者
|
||||
COINGATE_STATUS_REFUNDED = "refunded"
|
||||
//部分付款已退还给购物者。
|
||||
COINGATE_STATUS_PARTIALLY_REFUNDED = "partially_refunded"
|
||||
)
|
||||
11
common/global/exchange.go
Normal file
11
common/global/exchange.go
Normal file
@ -0,0 +1,11 @@
|
||||
package global
|
||||
|
||||
//交易所类型字典
|
||||
const (
|
||||
EXCHANGE_BINANCE = "binance"
|
||||
EXCHANGE_OKEX = "okex"
|
||||
EXCHANGE_GATE = "gate"
|
||||
EXCHANGE_COINBASE = "coinbase"
|
||||
EXCHANGE_BITFINEX = "bitfinex"
|
||||
EXCHANGE_BITMEX = "bitmex"
|
||||
)
|
||||
4
common/global/logo.go
Normal file
4
common/global/logo.go
Normal file
@ -0,0 +1,4 @@
|
||||
package global
|
||||
|
||||
// LogoContent go-admin ascii显示,减少静态文件依赖
|
||||
var LogoContent = []byte{10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 95, 95, 95, 95, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 44, 45, 45, 45, 44, 32, 32, 32, 32, 32, 32, 32, 32, 44, 39, 32, 32, 44, 32, 96, 46, 32, 32, 44, 45, 45, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 44, 45, 45, 45, 46, 32, 32, 32, 32, 32, 32, 44, 45, 45, 45, 44, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 44, 45, 45, 45, 46, 39, 124, 32, 32, 32, 32, 32, 44, 45, 43, 45, 44, 46, 39, 32, 95, 32, 124, 44, 45, 45, 46, 39, 124, 32, 32, 32, 32, 32, 32, 32, 32, 32, 44, 45, 45, 45, 44, 10, 32, 32, 44, 45, 45, 45, 45, 46, 95, 44, 46, 32, 32, 39, 32, 32, 32, 44, 39, 92, 32, 32, 32, 44, 39, 32, 32, 46, 39, 32, 124, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 124, 32, 32, 32, 124, 32, 58, 32, 32, 44, 45, 43, 45, 46, 32, 59, 32, 32, 32, 44, 32, 124, 124, 124, 32, 32, 124, 44, 32, 32, 32, 32, 32, 32, 44, 45, 43, 45, 46, 32, 47, 32, 32, 124, 10, 32, 47, 32, 32, 32, 47, 32, 32, 39, 32, 47, 32, 47, 32, 32, 32, 47, 32, 32, 32, 124, 44, 45, 45, 45, 46, 39, 32, 32, 32, 44, 32, 44, 45, 45, 46, 45, 45, 46, 32, 32, 32, 32, 32, 32, 124, 32, 32, 32, 124, 32, 124, 32, 44, 45, 45, 46, 39, 124, 39, 32, 32, 32, 124, 32, 32, 124, 124, 96, 45, 45, 39, 95, 32, 32, 32, 32, 32, 44, 45, 45, 46, 39, 124, 39, 32, 32, 32, 124, 10, 124, 32, 32, 32, 58, 32, 32, 32, 32, 32, 124, 46, 32, 32, 32, 59, 32, 44, 46, 32, 58, 124, 32, 32, 32, 124, 32, 32, 32, 32, 124, 47, 32, 32, 32, 32, 32, 32, 32, 92, 32, 32, 32, 44, 45, 45, 46, 95, 95, 124, 32, 124, 124, 32, 32, 32, 124, 32, 32, 44, 39, 44, 32, 124, 32, 32, 124, 44, 44, 39, 32, 44, 39, 124, 32, 32, 32, 124, 32, 32, 32, 124, 32, 32, 44, 34, 39, 32, 124, 10, 124, 32, 32, 32, 124, 32, 46, 92, 32, 32, 46, 39, 32, 32, 32, 124, 32, 124, 58, 32, 58, 58, 32, 32, 32, 58, 32, 32, 46, 39, 46, 45, 45, 46, 32, 32, 46, 45, 46, 32, 124, 32, 47, 32, 32, 32, 44, 39, 32, 32, 32, 124, 124, 32, 32, 32, 124, 32, 47, 32, 32, 124, 32, 124, 45, 45, 39, 32, 39, 32, 32, 124, 32, 124, 32, 32, 32, 124, 32, 32, 32, 124, 32, 47, 32, 32, 124, 32, 124, 10, 46, 32, 32, 32, 59, 32, 39, 59, 32, 32, 124, 39, 32, 32, 32, 124, 32, 46, 59, 32, 58, 58, 32, 32, 32, 124, 46, 39, 32, 32, 32, 92, 95, 95, 92, 47, 58, 32, 46, 32, 46, 46, 32, 32, 32, 39, 32, 32, 47, 32, 32, 124, 124, 32, 32, 32, 58, 32, 124, 32, 32, 124, 32, 44, 32, 32, 32, 32, 124, 32, 32, 124, 32, 58, 32, 32, 32, 124, 32, 32, 32, 124, 32, 124, 32, 32, 124, 32, 124, 10, 39, 32, 32, 32, 46, 32, 32, 32, 46, 32, 124, 124, 32, 32, 32, 58, 32, 32, 32, 32, 124, 96, 45, 45, 45, 39, 32, 32, 32, 32, 32, 44, 34, 32, 46, 45, 45, 46, 59, 32, 124, 39, 32, 32, 32, 59, 32, 124, 58, 32, 32, 124, 124, 32, 32, 32, 58, 32, 124, 32, 32, 124, 47, 32, 32, 32, 32, 32, 39, 32, 32, 58, 32, 124, 95, 95, 32, 124, 32, 32, 32, 124, 32, 124, 32, 32, 124, 47, 10, 32, 96, 45, 45, 45, 96, 45, 39, 124, 32, 124, 32, 92, 32, 32, 32, 92, 32, 32, 47, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 47, 32, 32, 47, 32, 32, 44, 46, 32, 32, 124, 124, 32, 32, 32, 124, 32, 39, 47, 32, 32, 39, 124, 32, 32, 32, 124, 32, 124, 96, 45, 39, 32, 32, 32, 32, 32, 32, 124, 32, 32, 124, 32, 39, 46, 39, 124, 124, 32, 32, 32, 124, 32, 124, 45, 45, 39, 10, 32, 46, 39, 95, 95, 47, 92, 95, 58, 32, 124, 32, 32, 96, 45, 45, 45, 45, 39, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 59, 32, 32, 58, 32, 32, 32, 46, 39, 32, 32, 32, 92, 32, 32, 32, 58, 32, 32, 32, 32, 58, 124, 124, 32, 32, 32, 59, 47, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 59, 32, 32, 58, 32, 32, 32, 32, 59, 124, 32, 32, 32, 124, 47, 10, 32, 124, 32, 32, 32, 58, 32, 32, 32, 32, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 124, 32, 32, 44, 32, 32, 32, 32, 32, 46, 45, 46, 47, 92, 32, 32, 32, 92, 32, 32, 47, 32, 32, 39, 45, 45, 45, 39, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 124, 32, 32, 44, 32, 32, 32, 47, 32, 39, 45, 45, 45, 39, 10, 32, 32, 92, 32, 32, 32, 92, 32, 32, 47, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96, 45, 45, 96, 45, 45, 45, 39, 32, 32, 32, 32, 32, 96, 45, 45, 45, 45, 39, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 96, 45, 39, 10, 32, 32, 32, 96, 45, 45, 96, 45, 39, 10}
|
||||
69
common/global/redis_prefix.go
Normal file
69
common/global/redis_prefix.go
Normal file
@ -0,0 +1,69 @@
|
||||
package global
|
||||
|
||||
const (
|
||||
//现货
|
||||
SPOT = "1"
|
||||
//合约
|
||||
FUT = "2"
|
||||
//现货-24h行情 {交易所类型code,交易对名称}
|
||||
TICKER_SPOT = "tc_spot:%s:%s"
|
||||
//合约-24h行情 {交易所类型code,交易对名称}
|
||||
TICKER_FUTURES = "tc_fut:%s:%s"
|
||||
//合约-资金费率
|
||||
FUNDING_INFO_FUTURES = "fi_fut"
|
||||
//合约-k线
|
||||
K_FUT = "k_fut"
|
||||
//现货-k线
|
||||
K_SPOT = "k_spot"
|
||||
//代币-配置
|
||||
COIN_DETAIL = "c_spot_detail"
|
||||
//代币-现货24h涨跌幅
|
||||
COIN_PRICE_CHANGE = "c_spot_priceChange"
|
||||
//代币-现货热门排序
|
||||
COIN_HOT_SORT = "c_spot_hot_sort"
|
||||
//代币-现货新币排序
|
||||
COIN_NEW_SORT = "c_spot_new_sort"
|
||||
//代币-现货主流排序
|
||||
COIN_MAIN_SORT = "c_spot_main_sort"
|
||||
|
||||
//代币-合约配置
|
||||
COIN_FUTURES_DETAIL = "c_fut_detail"
|
||||
//代币-合约24h涨跌幅
|
||||
COIN_FUTURES_PRICE_CHANGE = "c_fut_priceChange"
|
||||
//代币-合约热门排序
|
||||
COIN_FUTURES_HOT_SORT = "c_fut_hot_sort"
|
||||
//代币-合约新币排序
|
||||
COIN_FUTURES_NEW_SORT = "c_fut_new_sort"
|
||||
//代币-合约主流排序
|
||||
COIN_FUTURES_MAIN_SORT = "c_fut_main_sort"
|
||||
|
||||
//U本位合约-24h涨跌幅
|
||||
UFUTURES_PRICE_CHANGE = "ufut_priceChange"
|
||||
//币本位合约-24h涨跌幅
|
||||
CFUTURES_PRICE_CHANGE = "cfut_priceChange"
|
||||
|
||||
//用户订阅信息
|
||||
USER_SUBSCRIBE = "user_sub:%s" //{apikey}
|
||||
|
||||
//币种配置
|
||||
RECHARGE_COIN = "recharge_coin"
|
||||
)
|
||||
|
||||
const (
|
||||
//参数管理
|
||||
SYS_CONFIG = "sys_config"
|
||||
//字典数据
|
||||
DICT_DATA_PREFIX = "dic_list"
|
||||
)
|
||||
|
||||
const (
|
||||
//api websocket 错误信息
|
||||
API_WEBSOCKET_ERR = "api_ws_err:%s"
|
||||
)
|
||||
|
||||
const (
|
||||
//交易对-现货
|
||||
SYMBOL_SPOT = 0
|
||||
//交易对-合约
|
||||
SYMBOL_FUTURES = 1
|
||||
)
|
||||
60
common/global/state.go
Normal file
60
common/global/state.go
Normal file
@ -0,0 +1,60 @@
|
||||
package global
|
||||
|
||||
const (
|
||||
//币种配置-开启交易
|
||||
VTS_COIN_TRADE_Y = 1
|
||||
//币种配置-关闭交易
|
||||
VTS_COIN_TRADE_N = 2
|
||||
|
||||
//币种配置-已上架
|
||||
VTS_STATUS_Y = 3
|
||||
//币种配置-未上架
|
||||
VTS_STATUS_N = 1
|
||||
|
||||
//币种配置-显示在首页-是
|
||||
VTS_SHOW_HOME_Y = 1
|
||||
//币种配置-显示在首页-否
|
||||
VTS_SHOW_HOME_N = 2
|
||||
|
||||
// 币种配置-现货
|
||||
VTS_COIN_CATEGORY_SPOT = 0
|
||||
// 币种配置-合约
|
||||
VTS_COIN_CATEGORY_FUTURES = 1
|
||||
)
|
||||
|
||||
const (
|
||||
//api 启用
|
||||
AD_API_STATUS_Y = 1
|
||||
//api 禁用
|
||||
AD_API_STATUS_N = 2
|
||||
)
|
||||
|
||||
const (
|
||||
//代理-http
|
||||
PROXY_TYPE_HTTP = "http"
|
||||
////代理-https
|
||||
PROXY_TYPE_HTTPS = "https"
|
||||
////代理-socks5
|
||||
PROXY_TYPE_SOCKS5 = "socks5"
|
||||
)
|
||||
|
||||
const (
|
||||
//充值内容 线上钱包
|
||||
TRAN_TYPE_WALLET = 1
|
||||
//充值类型 内部
|
||||
TRAN_TYPE_ADMIN = 2
|
||||
)
|
||||
|
||||
const (
|
||||
//资金账户状态-正常
|
||||
OTC_HOLD_STATUS_NOMAL = 3
|
||||
//资金账户状态-冻结
|
||||
OTC_HOLD_STATUS_FREEZEN = 1
|
||||
)
|
||||
|
||||
const (
|
||||
//充值确认状态-未确认
|
||||
RECHARGE_CONFIRM_STATUS_UNCONFIRMED = "un_confirm"
|
||||
//充值确认状态-已确认
|
||||
RECHARGE_CONFIRM_STATUS_CONFIRMED = "confirmed"
|
||||
)
|
||||
7
common/global/topic.go
Normal file
7
common/global/topic.go
Normal file
@ -0,0 +1,7 @@
|
||||
package global
|
||||
|
||||
const (
|
||||
LoginLog = "login_log_queue"
|
||||
OperateLog = "operate_log_queue"
|
||||
ApiCheck = "api_check_queue"
|
||||
)
|
||||
282
common/helper/binancehttphelper.go
Normal file
282
common/helper/binancehttphelper.go
Normal file
@ -0,0 +1,282 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type BinanceClient struct {
|
||||
APIKey string
|
||||
APISecret string
|
||||
HTTPClient *http.Client
|
||||
SpotBaseURL string //现货api地址
|
||||
FutBaseURL string //合约api地址
|
||||
}
|
||||
|
||||
// NewBinanceClient creates a new Binance client
|
||||
func NewBinanceClient(apiKey, apiSecret string, proxyType, proxyAddr string) (*BinanceClient, error) {
|
||||
// Create HTTP client with transport settings
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 1000,
|
||||
IdleConnTimeout: 10 * time.Second, // 设置超时 10 秒
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
}
|
||||
err := CreateHtppProxy(proxyType, proxyAddr, client)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &BinanceClient{
|
||||
APIKey: apiKey,
|
||||
APISecret: apiSecret,
|
||||
HTTPClient: client,
|
||||
SpotBaseURL: "https://api.binance.com",
|
||||
FutBaseURL: "https://fapi.binance.com",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Helper to get proxy password from URL
|
||||
func getProxyPassword(proxyURL *url.URL) string {
|
||||
if password, ok := proxyURL.User.Password(); ok {
|
||||
return password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// signRequest generates HMAC SHA256 signature
|
||||
func (bc *BinanceClient) signRequest(query string) string {
|
||||
mac := hmac.New(sha256.New, []byte(bc.APISecret))
|
||||
mac.Write([]byte(query))
|
||||
return hex.EncodeToString(mac.Sum(nil))
|
||||
}
|
||||
|
||||
/*
|
||||
binance 现货请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendSpotRequest(endpoint string, method string, params map[string]string) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.SpotBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 0)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 现货请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendSpotRequestAuth(endpoint string, method string, params map[string]string) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.SpotBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 2)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 现货请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendSpotAuth(endpoint string, method string, params interface{}) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.SpotBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 2)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 现货请求 只需要api key
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendSpotRequestByKey(endpoint string, method string, params interface{}) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.SpotBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 1)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 合约请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendFuturesRequest(endpoint string, method string, params map[string]string) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.FutBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 0)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 合约请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendFuturesRequestAuth(endpoint string, method string, params map[string]string) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.FutBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 2)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 合约请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendFuturesRequestByKey(endpoint string, method string, params map[string]string) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.FutBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 1)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 合约请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendFuturesAuth(endpoint string, method string, params interface{}) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.FutBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 2)
|
||||
}
|
||||
|
||||
// SendRequest sends a request to Binance API
|
||||
/*
|
||||
发送请求
|
||||
- auth 0-不需要 1-只需要key 2-需要key和签名
|
||||
*/
|
||||
func (bc *BinanceClient) SendRequest(reqURL string, method string, params interface{}, auth int) ([]byte, int, error) {
|
||||
method = strings.ToUpper(method)
|
||||
reqParams := url.Values{}
|
||||
|
||||
// 处理 `params`,如果是 map[string]string 则添加为 URL 参数
|
||||
if paramMap, ok := params.(map[string]string); ok {
|
||||
for k, v := range paramMap {
|
||||
reqParams.Add(k, v)
|
||||
}
|
||||
} else if v := reflect.ValueOf(params); v.Kind() == reflect.Struct {
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := v.Type().Field(i)
|
||||
value := v.Field(i)
|
||||
|
||||
// 获取字段名或 JSON 标签名
|
||||
key := field.Tag.Get("json")
|
||||
if key == "" {
|
||||
key = field.Name // 如果没有 json 标签,使用字段名
|
||||
}
|
||||
|
||||
// 检查字段类型并转换为对应的字符串
|
||||
var strValue string
|
||||
switch value.Kind() {
|
||||
case reflect.String:
|
||||
strValue = value.String()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
strValue = strconv.FormatInt(value.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
strValue = strconv.FormatUint(value.Uint(), 10)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
strValue = strconv.FormatFloat(value.Float(), 'f', -1, 64)
|
||||
case reflect.Bool:
|
||||
strValue = strconv.FormatBool(value.Bool())
|
||||
case reflect.Struct:
|
||||
// 处理 decimal.Decimal 类型
|
||||
if value.Type() == reflect.TypeOf(decimal.Decimal{}) {
|
||||
strValue = value.Interface().(decimal.Decimal).String()
|
||||
} else {
|
||||
continue // 跳过其他 struct 类型
|
||||
}
|
||||
default:
|
||||
continue // 跳过不支持的类型
|
||||
}
|
||||
// 添加到 reqParams
|
||||
reqParams.Add(key, strValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Add timestamp if signature is needed
|
||||
if auth == 2 && bc.APIKey != "" && bc.APISecret != "" {
|
||||
reqParams.Add("timestamp", fmt.Sprintf("%d", time.Now().UnixMilli()))
|
||||
signature := bc.signRequest(reqParams.Encode())
|
||||
reqParams.Add("signature", signature)
|
||||
}
|
||||
// Create HTTP request
|
||||
var req *http.Request
|
||||
var err error
|
||||
if method == http.MethodGet || method == http.MethodDelete {
|
||||
if len(reqParams) > 0 {
|
||||
reqURL = fmt.Sprintf("%s?%s", reqURL, reqParams.Encode())
|
||||
}
|
||||
req, err = http.NewRequest(method, reqURL, nil)
|
||||
} else {
|
||||
// req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
if len(reqParams) > 0 {
|
||||
reqURL = fmt.Sprintf("%s?%s", reqURL, reqParams.Encode())
|
||||
}
|
||||
req, err = http.NewRequest(method, reqURL, nil)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, -1, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
// Set headers
|
||||
if auth > 0 && bc.APIKey != "" {
|
||||
req.Header.Set("X-MBX-APIKEY", bc.APIKey)
|
||||
}
|
||||
// req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
// Send request
|
||||
resp, err := bc.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, -1, fmt.Errorf("failed to send request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Read response
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, -1, fmt.Errorf("failed to read response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, resp.StatusCode, fmt.Errorf("%s", body)
|
||||
}
|
||||
|
||||
return body, http.StatusOK, nil
|
||||
}
|
||||
139
common/helper/cache_helper.go
Normal file
139
common/helper/cache_helper.go
Normal file
@ -0,0 +1,139 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
// CacheFunction 通用缓存装饰器,支持任意函数类型
|
||||
func CacheFunctionExpire[T any](fn func(args ...any) (T, error), keyPrefix string, ttl time.Duration) func(args ...any) (T, error) {
|
||||
return func(args ...any) (T, error) {
|
||||
// 创建缓存键,基于函数名和参数
|
||||
cacheKey := fmt.Sprintf("%s:%v", keyPrefix, args)
|
||||
|
||||
// 尝试从 Redis 获取缓存
|
||||
cachedData, err := DefaultRedis.GetString(cacheKey)
|
||||
var zeroT T // 用于返回零值
|
||||
if err == redis.Nil {
|
||||
// 缓存不存在,调用实际函数
|
||||
result, err := fn(args...)
|
||||
if err != nil {
|
||||
return zeroT, err
|
||||
}
|
||||
|
||||
// 将结果序列化并存入 Redis
|
||||
jsonData, err := sonic.Marshal(result)
|
||||
if err != nil {
|
||||
return zeroT, err
|
||||
}
|
||||
|
||||
err = DefaultRedis.SetStringExpire(cacheKey, string(jsonData), ttl)
|
||||
if err != nil {
|
||||
return zeroT, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
} else if err != nil {
|
||||
return zeroT, err
|
||||
}
|
||||
|
||||
// 缓存命中,反序列化结果
|
||||
var result T
|
||||
err = sonic.Unmarshal([]byte(cachedData), &result)
|
||||
if err != nil {
|
||||
return zeroT, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CacheFunction 通用缓存装饰器,支持任意函数类型
|
||||
func CacheFunctionNoArgsExpire[T any](fn func() (T, error), keyPrefix string, ttl time.Duration) func() (T, error) {
|
||||
return func() (T, error) {
|
||||
// 创建缓存键,基于函数名和参数
|
||||
cacheKey := keyPrefix
|
||||
|
||||
// 尝试从 Redis 获取缓存
|
||||
cachedData, err := DefaultRedis.GetString(cacheKey)
|
||||
var zeroT T // 用于返回零值
|
||||
if err == redis.Nil {
|
||||
// 缓存不存在,调用实际函数
|
||||
result, err := fn()
|
||||
if err != nil {
|
||||
return zeroT, err
|
||||
}
|
||||
|
||||
// 将结果序列化并存入 Redis
|
||||
jsonData, err := sonic.Marshal(result)
|
||||
if err != nil {
|
||||
return zeroT, err
|
||||
}
|
||||
|
||||
err = DefaultRedis.SetStringExpire(cacheKey, string(jsonData), ttl)
|
||||
if err != nil {
|
||||
return zeroT, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
} else if err != nil {
|
||||
return zeroT, err
|
||||
}
|
||||
|
||||
// 缓存命中,反序列化结果
|
||||
var result T
|
||||
err = sonic.Unmarshal([]byte(cachedData), &result)
|
||||
if err != nil {
|
||||
return zeroT, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCacheFunction 通用删除缓存装饰器
|
||||
func DeleteCacheFunction[T any](fn func(args ...any) (T, error), keyPrefix string) func(args ...any) (T, error) {
|
||||
return func(args ...any) (T, error) {
|
||||
// 调用实际函数
|
||||
result, err := fn(args...)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// 创建缓存键,基于函数名和参数
|
||||
cacheKey := fmt.Sprintf("%s:%v", keyPrefix, args)
|
||||
|
||||
// 从 Redis 删除缓存
|
||||
err = DefaultRedis.DeleteString(cacheKey)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("failed to delete cache: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCacheFunction 通用删除缓存装饰器
|
||||
func DeleteCacheNoArgsFunction[T any](fn func() (T, error), keyPrefix string) func() (T, error) {
|
||||
return func() (T, error) {
|
||||
// 调用实际函数
|
||||
result, err := fn()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// 创建缓存键,基于函数名和参数
|
||||
cacheKey := keyPrefix
|
||||
|
||||
// 从 Redis 删除缓存
|
||||
err = DefaultRedis.DeleteString(cacheKey)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("failed to delete cache: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
79
common/helper/proxy.go
Normal file
79
common/helper/proxy.go
Normal file
@ -0,0 +1,79 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
/*
|
||||
创建代理
|
||||
|
||||
- @proxyType 代理类别 http\https\socks5
|
||||
- @proxyAddr 代理地址 userName:password@endpoint:port
|
||||
*/
|
||||
func CreateHtppProxy(proxyType, proxyAddr string, client *http.Client) error {
|
||||
// Set up proxy based on type (HTTP, HTTPS, SOCKS5)
|
||||
transport := &http.Transport{}
|
||||
if proxyAddr != "" {
|
||||
if !strings.HasPrefix(proxyAddr, "http://") && !strings.HasPrefix(proxyAddr, "https://") && !strings.HasPrefix(proxyAddr, "socks5://") {
|
||||
proxyAddr = proxyType + "://" + proxyAddr
|
||||
}
|
||||
|
||||
if proxyType == "" {
|
||||
proxyType = strings.Split(proxyAddr, "://")[0]
|
||||
}
|
||||
|
||||
switch proxyType {
|
||||
case "http", "https":
|
||||
proxyURL, err := url.Parse(proxyAddr)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("invalid proxy URL: %w", err))
|
||||
}
|
||||
// Check if proxy URL contains credentials
|
||||
if proxyURL.User != nil {
|
||||
username := proxyURL.User.Username()
|
||||
password, _ := proxyURL.User.Password()
|
||||
transport.Proxy = func(req *http.Request) (*url.URL, error) {
|
||||
req.SetBasicAuth(username, password) // Set basic auth headers
|
||||
return proxyURL, nil
|
||||
}
|
||||
} else {
|
||||
transport.Proxy = http.ProxyURL(proxyURL)
|
||||
}
|
||||
case "socks5":
|
||||
proxyURL, err := url.Parse(proxyAddr)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("invalid proxy URL: %w", err))
|
||||
}
|
||||
|
||||
var auth *proxy.Auth
|
||||
if proxyURL.User != nil {
|
||||
auth = &proxy.Auth{
|
||||
User: proxyURL.User.Username(),
|
||||
Password: getProxyPassword(proxyURL),
|
||||
}
|
||||
}
|
||||
|
||||
dialer, err := proxy.SOCKS5("tcp", proxyURL.Host, auth, proxy.Direct)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("failed to set up SOCKS5 proxy: %w", err))
|
||||
}
|
||||
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return dialer.Dial(network, addr)
|
||||
}
|
||||
default:
|
||||
return errors.New(fmt.Sprintf("unsupported proxy type: %s", proxyType))
|
||||
}
|
||||
|
||||
client.Transport = transport
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
695
common/helper/redis_helper.go
Normal file
695
common/helper/redis_helper.go
Normal file
@ -0,0 +1,695 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
// RedisHelper 结构体封装了 Redis 客户端及上下文
|
||||
type RedisHelper struct {
|
||||
client *redis.Client // Redis 客户端
|
||||
ctx context.Context // 上下文
|
||||
emptyCacheValue string // 缓存空值的标志
|
||||
}
|
||||
|
||||
var DefaultRedis *RedisHelper
|
||||
|
||||
// 初始化默认链接
|
||||
func InitDefaultRedis(addr, password string, db int) {
|
||||
if DefaultRedis == nil {
|
||||
DefaultRedis = NewRedisHelper(addr, password, db)
|
||||
}
|
||||
|
||||
log.Printf("初始化redis链接")
|
||||
}
|
||||
|
||||
// NewRedisHelper 创建一个新的 RedisHelper 实例
|
||||
func NewRedisHelper(addr, password string, db int) *RedisHelper {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: addr, // Redis 服务器地址
|
||||
Password: password, // Redis 密码
|
||||
DB: db, // 使用的数据库编号
|
||||
PoolSize: 50,
|
||||
MinIdleConns: 10,
|
||||
DialTimeout: 10 * time.Second, // 调整连接超时时间
|
||||
ReadTimeout: 10 * time.Second, // 调整读超时时间
|
||||
WriteTimeout: 10 * time.Second, // 调整写超时时间
|
||||
})
|
||||
|
||||
return &RedisHelper{
|
||||
client: rdb,
|
||||
ctx: context.Background(), // 创建背景上下文
|
||||
}
|
||||
}
|
||||
|
||||
// 测试连接
|
||||
func (r *RedisHelper) Ping() error {
|
||||
return r.client.Ping(r.ctx).Err()
|
||||
}
|
||||
|
||||
// SetString 设置字符串值
|
||||
func (r *RedisHelper) SetString(key, value string) error {
|
||||
return r.client.Set(r.ctx, key, value, 0).Err() // 将值存储到指定的键
|
||||
}
|
||||
|
||||
// 批量设置
|
||||
func (r *RedisHelper) BatchSet(maps *map[string]string) error {
|
||||
pipe := r.client.Pipeline()
|
||||
|
||||
for key, val := range *maps {
|
||||
pipe.Set(r.ctx, key, val, 0)
|
||||
}
|
||||
|
||||
_, err := pipe.Exec(r.ctx)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SetString 设置字符串值
|
||||
func (r *RedisHelper) SetStringExpire(key, value string, expireTime time.Duration) error {
|
||||
return r.client.Set(r.ctx, key, value, expireTime).Err() // 将值存储到指定的键
|
||||
}
|
||||
|
||||
// SetString 设置字符串值
|
||||
func (r *RedisHelper) SetAdd(key, value string, expireTime time.Duration) error {
|
||||
// 存储到 SET 中
|
||||
result, err := r.client.SAdd(r.ctx, key, value).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if result == 1 {
|
||||
// 设置 SET 的过期时间
|
||||
err = r.client.Expire(r.ctx, key, expireTime).Err()
|
||||
if err != nil {
|
||||
return errors.New("设置过期时间失败:" + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("key已存在")
|
||||
}
|
||||
}
|
||||
|
||||
// 设置对象
|
||||
func SetObjString[T any](r *RedisHelper, key string, value T) error {
|
||||
keyValue, err := sonic.Marshal(value)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.SetString(key, string(keyValue))
|
||||
}
|
||||
|
||||
// 获取对象
|
||||
func GetObjString[T any](r *RedisHelper, key string) (T, error) {
|
||||
var result T
|
||||
value, err := r.GetString(key)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
err = sonic.Unmarshal([]byte(value), &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *RedisHelper) Get(key string) *redis.StringCmd {
|
||||
return r.client.Get(r.ctx, key)
|
||||
}
|
||||
|
||||
/*
|
||||
获取剩余时间
|
||||
- @key redis key
|
||||
*/
|
||||
func (r *RedisHelper) TTL(key string) *redis.DurationCmd {
|
||||
return r.client.TTL(r.ctx, key)
|
||||
}
|
||||
|
||||
// GetString 获取字符串值
|
||||
func (r *RedisHelper) GetString(key string) (string, error) {
|
||||
return r.client.Get(r.ctx, key).Result() // 从指定的键获取值
|
||||
}
|
||||
|
||||
// DeleteString 删除字符串键
|
||||
func (r *RedisHelper) DeleteString(key string) error {
|
||||
return r.client.Del(r.ctx, key).Err() // 删除指定的键
|
||||
}
|
||||
|
||||
// DeleteString 删除目录下所有key
|
||||
func (r *RedisHelper) DeleteAll(key string) error {
|
||||
keys, err := r.ScanKeys(key)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.BatchDeleteKeys(keys)
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
递增
|
||||
- @key rediskey
|
||||
*/
|
||||
func (r *RedisHelper) Incr(key string) *redis.IntCmd {
|
||||
return r.client.Incr(r.ctx, key)
|
||||
}
|
||||
|
||||
/*
|
||||
设置过期时间
|
||||
- @key redis key
|
||||
- @expiration 过期时间
|
||||
*/
|
||||
func (r *RedisHelper) Expire(key string, expiration time.Duration) *redis.BoolCmd {
|
||||
return r.client.Expire(r.ctx, key, expiration)
|
||||
}
|
||||
|
||||
/*
|
||||
批量删除
|
||||
|
||||
- @keys 键数组
|
||||
*/
|
||||
func (r *RedisHelper) BatchDeleteKeys(keys []string) (int, error) {
|
||||
if r.client == nil {
|
||||
return 0, errors.New("Redis client is nil")
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
deletedCount := 0
|
||||
batchSize := 1000 // 每批次删除的键数量
|
||||
for i := 0; i < len(keys); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(keys) {
|
||||
end = len(keys)
|
||||
}
|
||||
batch := keys[i:end]
|
||||
|
||||
_, err := r.client.Pipelined(r.ctx, func(pipe redis.Pipeliner) error {
|
||||
for _, key := range batch {
|
||||
pipe.Del(r.ctx, key)
|
||||
deletedCount++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return deletedCount, fmt.Errorf("failed to delete keys in batch: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return deletedCount, nil
|
||||
}
|
||||
|
||||
// DeleteKeysByPrefix 删除指定前缀的键
|
||||
func (r *RedisHelper) DeleteKeysByPrefix(prefixes ...string) error {
|
||||
ctx := context.Background()
|
||||
// 遍历每个前缀
|
||||
for _, prefix := range prefixes {
|
||||
var cursor uint64
|
||||
var keys []string
|
||||
|
||||
// 使用 SCAN 命令查找匹配的键
|
||||
for {
|
||||
var err error
|
||||
keys, cursor, err = r.client.Scan(ctx, cursor, prefix+"*", 1000).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除匹配的键
|
||||
if len(keys) > 0 {
|
||||
_, err := r.client.Del(ctx, keys...).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Deleted keys with prefix '%s': %v\n", prefix, keys)
|
||||
}
|
||||
|
||||
// 如果游标为 0,表示迭代结束
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 查找所有value
|
||||
func (r *RedisHelper) GetAllKeysAndValues(pattern string) ([]string, error) {
|
||||
var cursor uint64
|
||||
var result = []string{}
|
||||
|
||||
for {
|
||||
// 使用 SCAN 命令获取匹配的键
|
||||
keys, nextCursor, err := r.client.Scan(r.ctx, cursor, pattern+"*", 1000).Result()
|
||||
if err != nil {
|
||||
log.Printf("Error scanning keys: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 处理匹配到的键
|
||||
for _, key := range keys {
|
||||
value, err := r.client.Get(r.ctx, key).Result()
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
fmt.Printf("Key %s does not exist\n", key)
|
||||
} else {
|
||||
fmt.Printf("Error getting value for key %s: %v", key, err)
|
||||
}
|
||||
} else {
|
||||
result = append(result, value)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 cursor 为 0,表示扫描完成
|
||||
if nextCursor == 0 {
|
||||
break
|
||||
}
|
||||
cursor = nextCursor
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// LPushList 将一个或多个值插入到列表的头部
|
||||
func (r *RedisHelper) LPushList(key string, values ...string) error {
|
||||
return r.client.LPush(r.ctx, key, values).Err() // 将值插入到列表的头部
|
||||
}
|
||||
|
||||
// RPushList 将一个或多个值插入到列表的尾部
|
||||
func (r *RedisHelper) RPushList(key string, values ...string) error {
|
||||
return r.client.RPush(r.ctx, key, values).Err() // 将值插入到列表的尾部
|
||||
}
|
||||
|
||||
// LPopList 从列表的头部弹出一个元素
|
||||
func (r *RedisHelper) LPopList(key string) (string, error) {
|
||||
return r.client.LPop(r.ctx, key).Result() // 从列表的头部移除并返回第一个元素
|
||||
}
|
||||
|
||||
// RPopList 从列表的尾部弹出一个元素
|
||||
func (r *RedisHelper) RPopList(key string) (string, error) {
|
||||
return r.client.RPop(r.ctx, key).Result() // 从列表的尾部移除并返回最后一个元素
|
||||
}
|
||||
|
||||
// LRangeList 获取列表中指定范围的元素
|
||||
func (r *RedisHelper) LRangeList(key string, start, stop int64) ([]string, error) {
|
||||
return r.client.LRange(r.ctx, key, start, stop).Result() // 获取列表中指定范围的元素
|
||||
}
|
||||
|
||||
// GetAllList 获取列表中的所有元素
|
||||
func (r *RedisHelper) GetAllList(key string) ([]string, error) {
|
||||
values, err := r.client.LRange(r.ctx, key, 0, -1).Result()
|
||||
if err == redis.Nil {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 检查是否包含空值标志
|
||||
if len(values) == 1 && values[0] == r.emptyCacheValue {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func (r *RedisHelper) LRem(key, val string) (int64, error) {
|
||||
count := 0 // 删除所有与 valueToRemove 相等的元素
|
||||
result, err := r.client.LRem(r.ctx, key, int64(count), val).Result()
|
||||
if err != nil {
|
||||
fmt.Printf("删除元素失败: %v\n", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *RedisHelper) IsElementInList(key string, element string) (bool, error) {
|
||||
var cursor int64 = 0
|
||||
const batchSize int64 = 1000 // 每批次获取的元素数量
|
||||
|
||||
for {
|
||||
// 分批次获取列表元素
|
||||
elements, err := r.client.LRange(r.ctx, key, cursor, cursor+batchSize-1).Result()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(elements) == 0 {
|
||||
break // 没有更多数据
|
||||
}
|
||||
|
||||
// 遍历当前批次的元素
|
||||
for _, e := range elements {
|
||||
if e == element {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
cursor += batchSize // 移动到下一批次
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
/*
|
||||
SetListCache 重新设置列表缓存
|
||||
- @expiration 0-过期 1-过期时间
|
||||
*/
|
||||
func (r *RedisHelper) SetListCache(key string, expiration time.Duration, values ...string) error {
|
||||
tempKey := key + ":temp"
|
||||
|
||||
// 使用事务来确保操作的原子性
|
||||
pipe := r.client.TxPipeline()
|
||||
|
||||
// 将新数据插入到临时列表中
|
||||
pipe.RPush(r.ctx, tempKey, values)
|
||||
|
||||
// 重命名临时列表为目标列表
|
||||
pipe.Rename(r.ctx, tempKey, key)
|
||||
|
||||
if expiration > 0 {
|
||||
// 设置目标列表的过期时间
|
||||
pipe.Expire(r.ctx, key, expiration)
|
||||
}
|
||||
|
||||
// 执行事务
|
||||
_, err := pipe.Exec(r.ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetEmptyListCache 设置空值缓存
|
||||
func (r *RedisHelper) SetEmptyListCache(key string, expiration time.Duration) error {
|
||||
// 使用一个特殊标志值表示列表为空
|
||||
_, err := r.client.RPush(r.ctx, key, r.emptyCacheValue).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 设置列表的过期时间
|
||||
return r.client.Expire(r.ctx, key, expiration).Err()
|
||||
}
|
||||
|
||||
// scanKeys 使用 SCAN 命令获取所有匹配的键
|
||||
func (r *RedisHelper) ScanKeys(pattern string) ([]string, error) {
|
||||
|
||||
var cursor uint64
|
||||
var keys []string
|
||||
for {
|
||||
var newKeys []string
|
||||
var err error
|
||||
|
||||
// SCAN 命令每次返回部分匹配的键
|
||||
newKeys, cursor, err = r.client.Scan(r.ctx, cursor, pattern, 1000).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keys = append(keys, newKeys...)
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// 泛型函数,用于获取所有键的列表数据并合并为一个数组
|
||||
func GetAndMergeLists[T any](r *RedisHelper, keys []string) ([]T, error) {
|
||||
var combinedList []T
|
||||
for _, key := range keys {
|
||||
// 获取每个键的列表数据
|
||||
listData, err := r.client.LRange(r.ctx, key, 0, -1).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 解码每个数据项为类型 T,并添加到结果列表中
|
||||
for _, data := range listData {
|
||||
var item T
|
||||
if err := sonic.Unmarshal([]byte(data), &item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
combinedList = append(combinedList, item)
|
||||
}
|
||||
}
|
||||
return combinedList, nil
|
||||
}
|
||||
|
||||
// SetNX 实现类似于 Redis 的 SETNX 命令
|
||||
func (r *RedisHelper) SetNX(key string, value interface{}, expiration time.Duration) (bool, error) {
|
||||
result, err := r.client.Set(r.ctx, key, value, expiration).Result()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 如果键不存在则 result 会等于 "OK"
|
||||
return result == "OK", nil
|
||||
}
|
||||
|
||||
func getFieldsFromStruct(obj interface{}) map[string]interface{} {
|
||||
fields := make(map[string]interface{})
|
||||
val := reflect.ValueOf(obj)
|
||||
typ := reflect.TypeOf(obj)
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
tag := field.Tag.Get("redis")
|
||||
if tag != "" {
|
||||
fieldVal := val.Field(i)
|
||||
if fieldVal.Kind() == reflect.Slice || fieldVal.Kind() == reflect.Map {
|
||||
// 处理切片或映射类型
|
||||
// 对于切片,使用索引作为字段名
|
||||
if fieldVal.Kind() == reflect.Slice {
|
||||
for j := 0; j < fieldVal.Len(); j++ {
|
||||
elem := fieldVal.Index(j).Interface()
|
||||
fields[fmt.Sprintf("%s_%d", tag, j)] = elem
|
||||
}
|
||||
} else if fieldVal.Kind() == reflect.Map {
|
||||
// 对于映射,使用键作为字段名
|
||||
for _, key := range fieldVal.MapKeys() {
|
||||
elem := fieldVal.MapIndex(key).Interface()
|
||||
fields[fmt.Sprintf("%s_%v", tag, key.Interface())] = elem
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fields[tag] = fieldVal.Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func (r *RedisHelper) SetHashWithTags(key string, obj interface{}) error {
|
||||
fields := getFieldsFromStruct(obj)
|
||||
_, err := r.client.HSet(r.ctx, key, fields).Result()
|
||||
return err
|
||||
}
|
||||
|
||||
// HSetField 设置哈希中的一个字段
|
||||
func (r *RedisHelper) HSetField(key, field string, value interface{}) error {
|
||||
_, err := r.client.HSet(r.ctx, key, field, value).Result()
|
||||
if err != nil {
|
||||
log.Printf("Error setting field %s in hash %s: %v", field, key, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HSetMultipleFields 设置哈希中的多个字段
|
||||
func (r *RedisHelper) HSetMultipleFields(key string, fields map[string]interface{}) error {
|
||||
_, err := r.client.HSet(r.ctx, key, fields).Result()
|
||||
if err != nil {
|
||||
log.Printf("Error setting multiple fields in hash %s: %v", key, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HGetField 获取哈希中某个字段的值
|
||||
func (r *RedisHelper) HGetField(key, field string) (string, error) {
|
||||
val, err := r.client.HGet(r.ctx, key, field).Result()
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
return "", nil // Field does not exist
|
||||
}
|
||||
log.Printf("Error getting field %s from hash %s: %v", field, key, err)
|
||||
return "", err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// HGetAllFields 获取哈希中所有字段的值
|
||||
func (r *RedisHelper) HGetAllFields(key string) (map[string]string, error) {
|
||||
fields, err := r.client.HGetAll(r.ctx, key).Result()
|
||||
if err != nil {
|
||||
log.Printf("Error getting all fields from hash %s: %v", key, err)
|
||||
return nil, err
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// HDelField 删除哈希中的某个字段
|
||||
func (r *RedisHelper) HDelField(key, field string) error {
|
||||
_, err := r.client.HDel(r.ctx, key, field).Result()
|
||||
if err != nil {
|
||||
log.Printf("Error deleting field %s from hash %s: %v", field, key, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除哈希
|
||||
func (r *RedisHelper) HDelAll(key string) error {
|
||||
_, err := r.client.Del(r.ctx, key).Result()
|
||||
if err != nil {
|
||||
log.Printf("Error deleting from hash %s: %v", key, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HKeys 获取哈希中所有字段的名字
|
||||
func (r *RedisHelper) HKeys(key string) ([]string, error) {
|
||||
fields, err := r.client.HKeys(r.ctx, key).Result()
|
||||
if err != nil {
|
||||
log.Printf("Error getting keys from hash %s: %v", key, err)
|
||||
return nil, err
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// DelSet 从集合中删除元素
|
||||
func (r *RedisHelper) DelSet(key string, value string) error {
|
||||
_, err := r.client.SRem(r.ctx, key, value).Result()
|
||||
if err != nil {
|
||||
log.Printf("Error del value from set %s: %v", key, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RedisHelper) Sismember(key string, value string) (bool, error) {
|
||||
result, err := r.client.SIsMember(r.ctx, key, value).Result()
|
||||
if err != nil {
|
||||
log.Printf("Error Sismember value from set %s: %v", key, err)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// sort set start
|
||||
// 批量添加
|
||||
func (r *RedisHelper) BatchSortSet(key string, array []*redis.Z) error {
|
||||
pipe := r.client.Pipeline()
|
||||
for _, val := range array {
|
||||
pipe.ZAdd(r.ctx, key, val)
|
||||
}
|
||||
|
||||
_, err := pipe.Exec(r.ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// 单一写入 sort set
|
||||
func (e *RedisHelper) SignelAdd(key string, score float64, member string) error {
|
||||
// 先删除具有相同 score 的所有成员
|
||||
scoreStr := strconv.FormatFloat(score, 'g', -1, 64)
|
||||
_, err := e.client.ZRemRangeByScore(e.ctx, key, scoreStr, scoreStr).Result()
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("删除score失败", err.Error())
|
||||
}
|
||||
_, err = e.client.ZAdd(e.ctx, key, &redis.Z{
|
||||
Score: score,
|
||||
Member: member,
|
||||
}).Result()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 写入数据
|
||||
func (e *RedisHelper) AddSortSet(key string, score float64, member string) error {
|
||||
_, err := e.client.ZAdd(e.ctx, key, &redis.Z{
|
||||
Score: score,
|
||||
Member: member,
|
||||
}).Result()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除指定元素
|
||||
func (e *RedisHelper) DelSortSet(key, member string) error {
|
||||
return e.client.ZRem(e.ctx, key, member).Err()
|
||||
}
|
||||
|
||||
/*
|
||||
获取sort set 所有数据
|
||||
*/
|
||||
func (e *RedisHelper) GetAllSortSet(key string) ([]string, error) {
|
||||
return e.client.ZRange(e.ctx, key, 0, -1).Result()
|
||||
}
|
||||
|
||||
/*
|
||||
获取sort set 所有数据和score
|
||||
*/
|
||||
func (e *RedisHelper) GetRevRangeScoresSortSet(key string) ([]redis.Z, error) {
|
||||
return e.client.ZRevRangeWithScores(e.ctx, key, 0, -1).Result()
|
||||
}
|
||||
|
||||
// 获取指定区间数据
|
||||
func (e *RedisHelper) GetSortSetMembers(key string, start, stop int64) ([]string, error) {
|
||||
return e.client.ZRange(e.ctx, key, start, stop).Result()
|
||||
}
|
||||
|
||||
// 获取最后N条数据
|
||||
func (e *RedisHelper) GetLastSortSetMembers(key string, num int64) ([]string, error) {
|
||||
return e.client.ZRevRange(e.ctx, key, 0, num).Result()
|
||||
}
|
||||
|
||||
// func (e *RedisHelper) DelSortSet(key,)
|
||||
|
||||
// 根据索引范围删除
|
||||
func (e *RedisHelper) DelByRank(key string, start, stop int64) error {
|
||||
return e.client.ZRemRangeByRank(e.ctx, key, start, stop).Err()
|
||||
}
|
||||
|
||||
// sort set end
|
||||
|
||||
// GetUserLoginPwdErrFre 获取用户登录密码错误频次
|
||||
func (e *RedisHelper) GetUserLoginPwdErrFre(key string) (total int, wait time.Duration, err error) {
|
||||
total, _ = e.client.Get(e.ctx, key).Int()
|
||||
wait = e.client.TTL(e.ctx, key).Val()
|
||||
return
|
||||
}
|
||||
|
||||
// SetUserLoginPwdErrFre 设置用户登录密码错误频次
|
||||
func (e *RedisHelper) SetUserLoginPwdErrFre(key string, expire time.Duration) (val int64, err error) {
|
||||
val, err = e.client.Incr(e.ctx, key).Result()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = e.client.Expire(e.ctx, key, expire).Err(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
183
common/helper/redislock.go
Normal file
183
common/helper/redislock.go
Normal file
@ -0,0 +1,183 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"go-admin/pkg/utility"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
tolerance = 500 // milliseconds
|
||||
millisPerSecond = 1000
|
||||
lockCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
|
||||
redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2])
|
||||
return "OK"
|
||||
else
|
||||
return redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2])
|
||||
end`
|
||||
delCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
|
||||
return redis.call("DEL", KEYS[1])
|
||||
else
|
||||
return 0
|
||||
end`
|
||||
touchCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
|
||||
return redis.call("PEXPIRE", KEYS[1], ARGV[2])
|
||||
else
|
||||
return 0
|
||||
end`
|
||||
)
|
||||
|
||||
var (
|
||||
clientRedisLock *redis.Client
|
||||
onece sync.Once
|
||||
ErrFailed = errors.New("redsync: failed to acquire lock")
|
||||
)
|
||||
|
||||
// InitLockRedisConn 初始化 Redis 连接
|
||||
func InitLockRedisConn(host, password, dbIndex string) {
|
||||
onece.Do(func() {
|
||||
dbIndexInt, err := strconv.Atoi(dbIndex)
|
||||
if err != nil {
|
||||
dbIndexInt = 0
|
||||
}
|
||||
clientRedisLock = redis.NewClient(&redis.Options{
|
||||
Addr: host,
|
||||
Password: password,
|
||||
DB: dbIndexInt,
|
||||
})
|
||||
|
||||
// 测试连接
|
||||
if _, err := clientRedisLock.Ping(context.Background()).Result(); err != nil {
|
||||
log.Error("Failed to connect to Redis", zap.Error(err))
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// RedisLock 分布式锁结构
|
||||
type RedisLock struct {
|
||||
redisClient *redis.Client
|
||||
key string
|
||||
id string
|
||||
expiry time.Duration
|
||||
retryCount int // 重试次数
|
||||
retryInterval time.Duration // 重试间隔时间
|
||||
seconds uint32
|
||||
}
|
||||
|
||||
// NewRedisLock 创建一个新的 Redis 锁
|
||||
func NewRedisLock(key string, timeout uint32, retryCount int, retryInterval time.Duration) *RedisLock {
|
||||
return &RedisLock{
|
||||
redisClient: clientRedisLock,
|
||||
key: key,
|
||||
id: utility.GetXid(),
|
||||
expiry: time.Duration(timeout) * time.Second,
|
||||
retryCount: retryCount,
|
||||
retryInterval: retryInterval,
|
||||
seconds: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire 获取锁
|
||||
func (rl *RedisLock) Acquire() (bool, error) {
|
||||
return rl.acquireCtx(context.Background())
|
||||
}
|
||||
|
||||
// AcquireWait 获取锁,支持重试和超时
|
||||
func (rl *RedisLock) AcquireWait(ctx context.Context) (bool, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
for i := 0; i < rl.retryCount; i++ {
|
||||
if i > 0 {
|
||||
// 指数退避
|
||||
baseInterval := time.Duration(int64(rl.retryInterval) * (1 << i)) // 指数增长
|
||||
if baseInterval > time.Second {
|
||||
baseInterval = time.Second
|
||||
}
|
||||
|
||||
// 随机退避
|
||||
retryInterval := time.Duration(rand.Int63n(int64(baseInterval))) // 随机退避
|
||||
if retryInterval < time.Millisecond*100 {
|
||||
retryInterval = time.Millisecond * 100 // 至少 100ms
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false, ctx.Err()
|
||||
case <-time.After(retryInterval):
|
||||
}
|
||||
}
|
||||
|
||||
ok, err := rl.acquireCtx(ctx)
|
||||
if ok {
|
||||
return true, nil
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Failed to acquire lock", zap.String("key", rl.key), zap.Error(err))
|
||||
}
|
||||
}
|
||||
return false, ErrFailed
|
||||
}
|
||||
|
||||
// Release 释放锁
|
||||
func (rl *RedisLock) Release() (bool, error) {
|
||||
return rl.releaseCtx(context.Background())
|
||||
}
|
||||
|
||||
// Touch 续期锁
|
||||
func (rl *RedisLock) Touch() (bool, error) {
|
||||
return rl.touchCtx(context.Background())
|
||||
}
|
||||
|
||||
// acquireCtx 获取锁的核心逻辑
|
||||
func (rl *RedisLock) acquireCtx(ctx context.Context) (bool, error) {
|
||||
resp, err := rl.redisClient.Eval(ctx, lockCommand, []string{rl.key}, []string{
|
||||
rl.id, strconv.Itoa(int(rl.seconds)*millisPerSecond + tolerance),
|
||||
}).Result()
|
||||
if err == redis.Nil {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
log.Error("Error acquiring lock", zap.String("key", rl.key), zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
reply, ok := resp.(string)
|
||||
return ok && reply == "OK", nil
|
||||
}
|
||||
|
||||
// releaseCtx 释放锁的核心逻辑
|
||||
func (rl *RedisLock) releaseCtx(ctx context.Context) (bool, error) {
|
||||
resp, err := rl.redisClient.Eval(ctx, delCommand, []string{rl.key}, []string{rl.id}).Result()
|
||||
if err != nil {
|
||||
log.Error("Error releasing lock", zap.String("key", rl.key), zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
reply, ok := resp.(int64)
|
||||
return ok && reply == 1, nil
|
||||
}
|
||||
|
||||
// touchCtx 续期锁的核心逻辑
|
||||
func (rl *RedisLock) touchCtx(ctx context.Context) (bool, error) {
|
||||
seconds := atomic.LoadUint32(&rl.seconds)
|
||||
resp, err := rl.redisClient.Eval(ctx, touchCommand, []string{rl.key}, []string{
|
||||
rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance),
|
||||
}).Result()
|
||||
if err != nil {
|
||||
log.Error("Error touching lock", zap.String("key", rl.key), zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
reply, ok := resp.(int64)
|
||||
return ok && reply == 1, nil
|
||||
}
|
||||
50
common/helper/snowflake.go
Normal file
50
common/helper/snowflake.go
Normal file
@ -0,0 +1,50 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
extendConfig "go-admin/config"
|
||||
|
||||
"github.com/bwmarrin/snowflake"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
)
|
||||
|
||||
// 全局雪花节点实例
|
||||
var (
|
||||
node *snowflake.Node
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func InitSnowflakeNode() error {
|
||||
var err error
|
||||
|
||||
node, err = snowflake.NewNode(extendConfig.ExtConfig.ServiceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取订单雪花id
|
||||
func GetOrderNo() string {
|
||||
if node == nil {
|
||||
once.Do(func() {
|
||||
if node == nil {
|
||||
if err := InitSnowflakeNode(); err != nil {
|
||||
log.Fatalf("初始化雪花算法节点失败: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if node == nil {
|
||||
log.Fatal("雪花算法节点未初始化")
|
||||
}
|
||||
|
||||
orderID := node.Generate()
|
||||
idStr := strconv.FormatInt(orderID.Int64(), 10)
|
||||
|
||||
return idStr
|
||||
}
|
||||
27
common/ip.go
Normal file
27
common/ip.go
Normal file
@ -0,0 +1,27 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetClientIP(c *gin.Context) string {
|
||||
ClientIP := c.ClientIP()
|
||||
//fmt.Println("ClientIP:", ClientIP)
|
||||
RemoteIP := c.RemoteIP()
|
||||
//fmt.Println("RemoteIP:", RemoteIP)
|
||||
ip := c.Request.Header.Get("X-Forwarded-For")
|
||||
if strings.Contains(ip, "127.0.0.1") || ip == "" {
|
||||
ip = c.Request.Header.Get("X-real-ip")
|
||||
}
|
||||
if ip == "" {
|
||||
ip = "127.0.0.1"
|
||||
}
|
||||
if RemoteIP != "127.0.0.1" {
|
||||
ip = RemoteIP
|
||||
}
|
||||
if ClientIP != "127.0.0.1" {
|
||||
ip = ClientIP
|
||||
}
|
||||
return ip
|
||||
}
|
||||
104
common/middleware/auth.go
Normal file
104
common/middleware/auth.go
Normal file
@ -0,0 +1,104 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
"go-admin/common/middleware/dto"
|
||||
"go-admin/common/service/sysservice/sysstatuscode"
|
||||
statuscode "go-admin/common/status_code"
|
||||
"go-admin/pkg/cryptohelper/jwthelper"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/sdk/config"
|
||||
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||
"go-admin/common/middleware/handler"
|
||||
)
|
||||
|
||||
// AuthInit jwt验证new
|
||||
func AuthInit() (*jwt.GinJWTMiddleware, error) {
|
||||
timeout := time.Hour
|
||||
if config.ApplicationConfig.Mode == "dev" {
|
||||
timeout = time.Duration(876010) * time.Hour
|
||||
} else {
|
||||
if config.JwtConfig.Timeout != 0 {
|
||||
timeout = time.Duration(config.JwtConfig.Timeout) * time.Second
|
||||
}
|
||||
}
|
||||
return jwt.New(&jwt.GinJWTMiddleware{
|
||||
Realm: "test zone",
|
||||
Key: []byte(config.JwtConfig.Secret),
|
||||
Timeout: timeout,
|
||||
MaxRefresh: time.Hour,
|
||||
PayloadFunc: handler.PayloadFunc,
|
||||
IdentityHandler: handler.IdentityHandler,
|
||||
Authenticator: handler.Authenticator,
|
||||
Authorizator: handler.Authorizator,
|
||||
Unauthorized: handler.Unauthorized,
|
||||
TokenLookup: "header: Authorization, query: token, cookie: jwt",
|
||||
TokenHeadName: "Bearer",
|
||||
TimeFunc: time.Now,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func FrontedAuth(c *gin.Context) {
|
||||
// 从请求头中获取 token 和 os
|
||||
token := c.GetHeader("Authorization")
|
||||
source, _ := strconv.Atoi(c.GetHeader("os"))
|
||||
// 如果 token 不存在,返回未登录的状态
|
||||
if len(token) == 0 {
|
||||
err := ResponseWithStatus(c, dto.NotLoginStatus, statuscode.NotLoggedIn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.Abort() // 停止后续中间件的执行
|
||||
return
|
||||
}
|
||||
// 验证 token 并获取结果
|
||||
flag, rew := jwthelper.MidValidToken(token, source)
|
||||
if flag < 0 || len(rew) == 0 {
|
||||
if flag == -1 {
|
||||
ResponseWithStatus(c, dto.NotLoginStatus, statuscode.NotLoggedIn)
|
||||
} else if flag == -2 {
|
||||
ResponseWithStatus(c, dto.ReLoginStatus, statuscode.ReLogin)
|
||||
}
|
||||
c.Abort() // 停止后续中间件的执行
|
||||
return
|
||||
}
|
||||
// 将解析后的 token 设置到请求头中
|
||||
c.Request.Header.Set("ParseToken", rew)
|
||||
// 继续处理请求
|
||||
c.Next()
|
||||
}
|
||||
|
||||
// ResponseWithStatus 带状态的响应
|
||||
func ResponseWithStatus(ctx *gin.Context, status int, code int, data ...interface{}) error {
|
||||
// 获取语言对应的 msg
|
||||
msg := sysstatuscode.GetStatusCodeDescription(ctx, code)
|
||||
if msg == `` {
|
||||
msg = strconv.Itoa(code)
|
||||
} else {
|
||||
// 配置了语言包参数
|
||||
if strings.Contains(msg, "%") && len(data) > 1 {
|
||||
msg = fmt.Sprintf(msg, data[1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
resp := dto.Response{
|
||||
Status: status,
|
||||
Code: code,
|
||||
Msg: msg,
|
||||
}
|
||||
|
||||
resp.RequestID = pkg.GenerateMsgIDFromContext(ctx)
|
||||
if len(data) > 0 {
|
||||
resp.Data = data[0]
|
||||
}
|
||||
|
||||
ctx.JSON(200, resp)
|
||||
|
||||
return nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user