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)
|
||||
}
|
||||
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,
|
||||
}
|
||||
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")
|
||||
}
|
||||
11
common/global/adm.go
Normal file
11
common/global/adm.go
Normal file
@ -0,0 +1,11 @@
|
||||
package global
|
||||
|
||||
const (
|
||||
// Version go-admin version info
|
||||
Version = "2.1.2"
|
||||
)
|
||||
|
||||
var (
|
||||
// Driver 数据库驱动
|
||||
Driver string
|
||||
)
|
||||
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
|
||||
}
|
||||
}
|
||||
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}
|
||||
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"
|
||||
)
|
||||
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
|
||||
}
|
||||
36
common/middleware/auth.go
Normal file
36
common/middleware/auth.go
Normal file
@ -0,0 +1,36 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"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,
|
||||
})
|
||||
|
||||
}
|
||||
61
common/middleware/customerror.go
Normal file
61
common/middleware/customerror.go
Normal file
@ -0,0 +1,61 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func CustomError(c *gin.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
|
||||
if c.IsAborted() {
|
||||
c.Status(200)
|
||||
}
|
||||
switch errStr := err.(type) {
|
||||
case string:
|
||||
p := strings.Split(errStr, "#")
|
||||
if len(p) == 3 && p[0] == "CustomError" {
|
||||
statusCode, e := strconv.Atoi(p[1])
|
||||
if e != nil {
|
||||
break
|
||||
}
|
||||
c.Status(statusCode)
|
||||
fmt.Println(
|
||||
time.Now().Format("2006-01-02 15:04:05"),
|
||||
"[ERROR]",
|
||||
c.Request.Method,
|
||||
c.Request.URL,
|
||||
statusCode,
|
||||
c.Request.RequestURI,
|
||||
c.ClientIP(),
|
||||
p[2],
|
||||
)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": statusCode,
|
||||
"msg": p[2],
|
||||
})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": errStr,
|
||||
})
|
||||
}
|
||||
case runtime.Error:
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": errStr.Error(),
|
||||
})
|
||||
default:
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
c.Next()
|
||||
}
|
||||
11
common/middleware/db.go
Normal file
11
common/middleware/db.go
Normal file
@ -0,0 +1,11 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
)
|
||||
|
||||
func WithContextDb(c *gin.Context) {
|
||||
c.Set("db", sdk.Runtime.GetDbByKey(c.Request.Host).WithContext(c))
|
||||
c.Next()
|
||||
}
|
||||
29
common/middleware/demo.go
Normal file
29
common/middleware/demo.go
Normal file
@ -0,0 +1,29 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/config"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func DemoEvn() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
if config.ApplicationConfig.Mode == "demo" {
|
||||
if method == "GET" ||
|
||||
method == "OPTIONS" ||
|
||||
c.Request.RequestURI == "/api/v1/login" ||
|
||||
c.Request.RequestURI == "/api/v1/logout" {
|
||||
c.Next()
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "谢谢您的参与,但为了大家更好的体验,所以本次提交就算了吧!\U0001F600\U0001F600\U0001F600",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
182
common/middleware/handler/auth.go
Normal file
182
common/middleware/handler/auth.go
Normal file
@ -0,0 +1,182 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"go-admin/app/admin/models"
|
||||
"go-admin/common"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
"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/captcha"
|
||||
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
"github.com/mssola/user_agent"
|
||||
"go-admin/common/global"
|
||||
)
|
||||
|
||||
func PayloadFunc(data interface{}) jwt.MapClaims {
|
||||
if v, ok := data.(map[string]interface{}); ok {
|
||||
u, _ := v["user"].(SysUser)
|
||||
r, _ := v["role"].(SysRole)
|
||||
return jwt.MapClaims{
|
||||
jwt.IdentityKey: u.UserId,
|
||||
jwt.RoleIdKey: r.RoleId,
|
||||
jwt.RoleKey: r.RoleKey,
|
||||
jwt.NiceKey: u.Username,
|
||||
jwt.DataScopeKey: r.DataScope,
|
||||
jwt.RoleNameKey: r.RoleName,
|
||||
}
|
||||
}
|
||||
return jwt.MapClaims{}
|
||||
}
|
||||
|
||||
func IdentityHandler(c *gin.Context) interface{} {
|
||||
claims := jwt.ExtractClaims(c)
|
||||
return map[string]interface{}{
|
||||
"IdentityKey": claims["identity"],
|
||||
"UserName": claims["nice"],
|
||||
"RoleKey": claims["rolekey"],
|
||||
"UserId": claims["identity"],
|
||||
"RoleIds": claims["roleid"],
|
||||
"DataScope": claims["datascope"],
|
||||
}
|
||||
}
|
||||
|
||||
// Authenticator 获取token
|
||||
// @Summary 登陆
|
||||
// @Description 获取token
|
||||
// @Description LoginHandler can be used by clients to get a jwt token.
|
||||
// @Description Payload needs to be json in the form of {"username": "USERNAME", "password": "PASSWORD"}.
|
||||
// @Description Reply will be of the form {"token": "TOKEN"}.
|
||||
// @Description dev mode:It should be noted that all fields cannot be empty, and a value of 0 can be passed in addition to the account password
|
||||
// @Description 注意:开发模式:需要注意全部字段不能为空,账号密码外可以传入0值
|
||||
// @Tags 登陆
|
||||
// @Accept application/json
|
||||
// @Product application/json
|
||||
// @Param account body Login true "account"
|
||||
// @Success 200 {string} string "{"code": 200, "expire": "2019-08-07T12:45:48+08:00", "token": ".eyJleHAiOjE1NjUxNTMxNDgsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU2NTE0OTU0OH0.-zvzHvbg0A" }"
|
||||
// @Router /api/v1/login [post]
|
||||
func Authenticator(c *gin.Context) (interface{}, error) {
|
||||
log := api.GetRequestLogger(c)
|
||||
db, err := pkg.GetOrm(c)
|
||||
if err != nil {
|
||||
log.Errorf("get db error, %s", err.Error())
|
||||
response.Error(c, 500, err, "数据库连接获取失败")
|
||||
return nil, jwt.ErrFailedAuthentication
|
||||
}
|
||||
|
||||
var loginVals Login
|
||||
var status = "2"
|
||||
var msg = "登录成功"
|
||||
var username = ""
|
||||
defer func() {
|
||||
LoginLogToDB(c, status, msg, username)
|
||||
}()
|
||||
|
||||
if err = c.ShouldBind(&loginVals); err != nil {
|
||||
username = loginVals.Username
|
||||
msg = "数据解析失败"
|
||||
status = "1"
|
||||
|
||||
return nil, jwt.ErrMissingLoginValues
|
||||
}
|
||||
if config.ApplicationConfig.Mode != "dev" {
|
||||
if !captcha.Verify(loginVals.UUID, loginVals.Code, true) {
|
||||
username = loginVals.Username
|
||||
msg = "验证码错误"
|
||||
status = "1"
|
||||
|
||||
return nil, jwt.ErrInvalidVerificationode
|
||||
}
|
||||
}
|
||||
sysUser, role, e := loginVals.GetUser(db)
|
||||
if e == nil {
|
||||
username = loginVals.Username
|
||||
|
||||
return map[string]interface{}{"user": sysUser, "role": role}, nil
|
||||
} else {
|
||||
msg = "登录失败"
|
||||
status = "1"
|
||||
log.Warnf("%s login failed!", loginVals.Username)
|
||||
}
|
||||
return nil, jwt.ErrFailedAuthentication
|
||||
}
|
||||
|
||||
// LoginLogToDB Write log to database
|
||||
func LoginLogToDB(c *gin.Context, status string, msg string, username string) {
|
||||
if !config.LoggerConfig.EnabledDB {
|
||||
return
|
||||
}
|
||||
log := api.GetRequestLogger(c)
|
||||
l := make(map[string]interface{})
|
||||
|
||||
ua := user_agent.New(c.Request.UserAgent())
|
||||
l["ipaddr"] = common.GetClientIP(c)
|
||||
l["loginLocation"] = "" // pkg.GetLocation(common.GetClientIP(c),gaConfig.ExtConfig.AMap.Key)
|
||||
l["loginTime"] = pkg.GetCurrentTime()
|
||||
l["status"] = status
|
||||
l["remark"] = c.Request.UserAgent()
|
||||
browserName, browserVersion := ua.Browser()
|
||||
l["browser"] = browserName + " " + browserVersion
|
||||
l["os"] = ua.OS()
|
||||
l["platform"] = ua.Platform()
|
||||
l["username"] = username
|
||||
l["msg"] = msg
|
||||
|
||||
q := sdk.Runtime.GetMemoryQueue(c.Request.Host)
|
||||
message, err := sdk.Runtime.GetStreamMessage("", global.LoginLog, l)
|
||||
if err != nil {
|
||||
log.Errorf("GetStreamMessage error, %s", err.Error())
|
||||
//日志报错错误,不中断请求
|
||||
} else {
|
||||
err = q.Append(message)
|
||||
if err != nil {
|
||||
log.Errorf("Append message error, %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LogOut
|
||||
// @Summary 退出登录
|
||||
// @Description 获取token
|
||||
// LoginHandler can be used by clients to get a jwt token.
|
||||
// Reply will be of the form {"token": "TOKEN"}.
|
||||
// @Accept application/json
|
||||
// @Product application/json
|
||||
// @Success 200 {string} string "{"code": 200, "msg": "成功退出系统" }"
|
||||
// @Router /logout [post]
|
||||
// @Security Bearer
|
||||
func LogOut(c *gin.Context) {
|
||||
LoginLogToDB(c, "2", "退出成功", user.GetUserName(c))
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"msg": "退出成功",
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Authorizator(data interface{}, c *gin.Context) bool {
|
||||
|
||||
if v, ok := data.(map[string]interface{}); ok {
|
||||
u, _ := v["user"].(models.SysUser)
|
||||
r, _ := v["role"].(models.SysRole)
|
||||
c.Set("role", r.RoleName)
|
||||
c.Set("roleIds", r.RoleId)
|
||||
c.Set("userId", u.UserId)
|
||||
c.Set("userName", u.Username)
|
||||
c.Set("dataScope", r.DataScope)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Unauthorized(c *gin.Context, code int, message string) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"msg": message,
|
||||
})
|
||||
}
|
||||
22
common/middleware/handler/httpshandler.go
Normal file
22
common/middleware/handler/httpshandler.go
Normal file
@ -0,0 +1,22 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/unrolled/secure"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/sdk/config"
|
||||
)
|
||||
|
||||
func TlsHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
secureMiddleware := secure.New(secure.Options{
|
||||
SSLRedirect: true,
|
||||
SSLHost: config.SslConfig.Domain,
|
||||
})
|
||||
err := secureMiddleware.Process(c.Writer, c.Request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
33
common/middleware/handler/login.go
Normal file
33
common/middleware/handler/login.go
Normal file
@ -0,0 +1,33 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Login struct {
|
||||
Username string `form:"UserName" json:"username" binding:"required"`
|
||||
Password string `form:"Password" json:"password" binding:"required"`
|
||||
Code string `form:"Code" json:"code" binding:"required"`
|
||||
UUID string `form:"UUID" json:"uuid" binding:"required"`
|
||||
}
|
||||
|
||||
func (u *Login) GetUser(tx *gorm.DB) (user SysUser, role SysRole, err error) {
|
||||
err = tx.Table("sys_user").Where("username = ? and status = '2'", u.Username).First(&user).Error
|
||||
if err != nil {
|
||||
log.Errorf("get user error, %s", err.Error())
|
||||
return
|
||||
}
|
||||
_, err = pkg.CompareHashAndPassword(user.Password, u.Password)
|
||||
if err != nil {
|
||||
log.Errorf("user login error, %s", err.Error())
|
||||
return
|
||||
}
|
||||
err = tx.Table("sys_role").Where("role_id = ? ", user.RoleId).First(&role).Error
|
||||
if err != nil {
|
||||
log.Errorf("get role error, %s", err.Error())
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
11
common/middleware/handler/ping.go
Normal file
11
common/middleware/handler/ping.go
Normal file
@ -0,0 +1,11 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Ping(c *gin.Context) {
|
||||
c.JSON(200, gin.H{
|
||||
"message": "ok",
|
||||
})
|
||||
}
|
||||
24
common/middleware/handler/role.go
Normal file
24
common/middleware/handler/role.go
Normal file
@ -0,0 +1,24 @@
|
||||
package handler
|
||||
|
||||
import "go-admin/common/models"
|
||||
|
||||
type SysRole struct {
|
||||
RoleId int `json:"roleId" gorm:"primaryKey;autoIncrement"` // 角色编码
|
||||
RoleName string `json:"roleName" gorm:"size:128;"` // 角色名称
|
||||
Status string `json:"status" gorm:"size:4;"` //
|
||||
RoleKey string `json:"roleKey" gorm:"size:128;"` //角色代码
|
||||
RoleSort int `json:"roleSort" gorm:""` //角色排序
|
||||
Flag string `json:"flag" gorm:"size:128;"` //
|
||||
Remark string `json:"remark" gorm:"size:255;"` //备注
|
||||
Admin bool `json:"admin" gorm:"size:4;"`
|
||||
DataScope string `json:"dataScope" gorm:"size:128;"`
|
||||
Params string `json:"params" gorm:"-"`
|
||||
MenuIds []int `json:"menuIds" gorm:"-"`
|
||||
DeptIds []int `json:"deptIds" gorm:"-"`
|
||||
models.ControlBy
|
||||
models.ModelTime
|
||||
}
|
||||
|
||||
func (SysRole) TableName() string {
|
||||
return "sys_role"
|
||||
}
|
||||
40
common/middleware/handler/user.go
Normal file
40
common/middleware/handler/user.go
Normal file
@ -0,0 +1,40 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"go-admin/common/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type SysUser struct {
|
||||
UserId int `gorm:"primaryKey;autoIncrement;comment:编码" json:"userId"`
|
||||
Username string `json:"username" gorm:"size:64;comment:用户名"`
|
||||
Password string `json:"-" gorm:"size:128;comment:密码"`
|
||||
NickName string `json:"nickName" gorm:"size:128;comment:昵称"`
|
||||
Phone string `json:"phone" gorm:"size:11;comment:手机号"`
|
||||
RoleId int `json:"roleId" gorm:"size:20;comment:角色ID"`
|
||||
Salt string `json:"-" gorm:"size:255;comment:加盐"`
|
||||
Avatar string `json:"avatar" gorm:"size:255;comment:头像"`
|
||||
Sex string `json:"sex" gorm:"size:255;comment:性别"`
|
||||
Email string `json:"email" gorm:"size:128;comment:邮箱"`
|
||||
DeptId int `json:"deptId" gorm:"size:20;comment:部门"`
|
||||
PostId int `json:"postId" gorm:"size:20;comment:岗位"`
|
||||
Remark string `json:"remark" gorm:"size:255;comment:备注"`
|
||||
Status string `json:"status" gorm:"size:4;comment:状态"`
|
||||
DeptIds []int `json:"deptIds" gorm:"-"`
|
||||
PostIds []int `json:"postIds" gorm:"-"`
|
||||
RoleIds []int `json:"roleIds" gorm:"-"`
|
||||
//Dept *SysDept `json:"dept"`
|
||||
models.ControlBy
|
||||
models.ModelTime
|
||||
}
|
||||
|
||||
func (*SysUser) TableName() string {
|
||||
return "sys_user"
|
||||
}
|
||||
|
||||
func (e *SysUser) AfterFind(_ *gorm.DB) error {
|
||||
e.DeptIds = []int{e.DeptId}
|
||||
e.PostIds = []int{e.PostId}
|
||||
e.RoleIds = []int{e.RoleId}
|
||||
return nil
|
||||
}
|
||||
48
common/middleware/header.go
Normal file
48
common/middleware/header.go
Normal file
@ -0,0 +1,48 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// NoCache is a middleware function that appends headers
|
||||
// to prevent the client from caching the HTTP response.
|
||||
func NoCache(c *gin.Context) {
|
||||
c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
|
||||
c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
|
||||
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
|
||||
c.Next()
|
||||
}
|
||||
|
||||
// Options is a middleware function that appends headers
|
||||
// for options requests and aborts then exits the middleware
|
||||
// chain and ends the request.
|
||||
func Options(c *gin.Context) {
|
||||
if c.Request.Method != "OPTIONS" {
|
||||
c.Next()
|
||||
} else {
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
|
||||
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
|
||||
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
|
||||
c.Header("Content-Type", "application/json")
|
||||
c.AbortWithStatus(200)
|
||||
}
|
||||
}
|
||||
|
||||
// Secure is a middleware function that appends security
|
||||
// and resource access headers.
|
||||
func Secure(c *gin.Context) {
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
//c.Header("X-Frame-Options", "DENY")
|
||||
c.Header("X-Content-Type-Options", "nosniff")
|
||||
c.Header("X-XSS-Protection", "1; mode=block")
|
||||
if c.Request.TLS != nil {
|
||||
c.Header("Strict-Transport-Security", "max-age=31536000")
|
||||
}
|
||||
|
||||
// Also consider adding Content-Security-Policy headers
|
||||
// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
|
||||
}
|
||||
35
common/middleware/init.go
Normal file
35
common/middleware/init.go
Normal file
@ -0,0 +1,35 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||
"go-admin/common/actions"
|
||||
)
|
||||
|
||||
const (
|
||||
JwtTokenCheck string = "JwtToken"
|
||||
RoleCheck string = "AuthCheckRole"
|
||||
PermissionCheck string = "PermissionAction"
|
||||
)
|
||||
|
||||
func InitMiddleware(r *gin.Engine) {
|
||||
r.Use(DemoEvn())
|
||||
// 数据库链接
|
||||
r.Use(WithContextDb)
|
||||
// 日志处理
|
||||
r.Use(LoggerToFile())
|
||||
// 自定义错误处理
|
||||
r.Use(CustomError)
|
||||
// NoCache is a middleware function that appends headers
|
||||
r.Use(NoCache)
|
||||
// 跨域处理
|
||||
r.Use(Options)
|
||||
// Secure is a middleware function that appends security
|
||||
r.Use(Secure)
|
||||
// 链路追踪
|
||||
//r.Use(middleware.Trace())
|
||||
sdk.Runtime.SetMiddleware(JwtTokenCheck, (*jwt.GinJWTMiddleware).MiddlewareFunc)
|
||||
sdk.Runtime.SetMiddleware(RoleCheck, AuthCheckRole())
|
||||
sdk.Runtime.SetMiddleware(PermissionCheck, actions.PermissionAction())
|
||||
}
|
||||
136
common/middleware/logger.go
Normal file
136
common/middleware/logger.go
Normal file
@ -0,0 +1,136 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"go-admin/app/admin/service/dto"
|
||||
"go-admin/common"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/config"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
||||
|
||||
"go-admin/common/global"
|
||||
)
|
||||
|
||||
// LoggerToFile 日志记录到文件
|
||||
func LoggerToFile() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
log := api.GetRequestLogger(c)
|
||||
// 开始时间
|
||||
startTime := time.Now()
|
||||
// 处理请求
|
||||
var body string
|
||||
switch c.Request.Method {
|
||||
case http.MethodPost, http.MethodPut, http.MethodGet, http.MethodDelete:
|
||||
bf := bytes.NewBuffer(nil)
|
||||
wt := bufio.NewWriter(bf)
|
||||
_, err := io.Copy(wt, c.Request.Body)
|
||||
if err != nil {
|
||||
log.Warnf("copy body error, %s", err.Error())
|
||||
err = nil
|
||||
}
|
||||
rb, _ := ioutil.ReadAll(bf)
|
||||
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(rb))
|
||||
body = string(rb)
|
||||
}
|
||||
|
||||
c.Next()
|
||||
url := c.Request.RequestURI
|
||||
if strings.Index(url, "logout") > -1 ||
|
||||
strings.Index(url, "login") > -1 {
|
||||
return
|
||||
}
|
||||
// 结束时间
|
||||
endTime := time.Now()
|
||||
if c.Request.Method == http.MethodOptions {
|
||||
return
|
||||
}
|
||||
|
||||
rt, bl := c.Get("result")
|
||||
var result = ""
|
||||
if bl {
|
||||
rb, err := json.Marshal(rt)
|
||||
if err != nil {
|
||||
log.Warnf("json Marshal result error, %s", err.Error())
|
||||
} else {
|
||||
result = string(rb)
|
||||
}
|
||||
}
|
||||
|
||||
st, bl := c.Get("status")
|
||||
var statusBus = 0
|
||||
if bl {
|
||||
statusBus = st.(int)
|
||||
}
|
||||
|
||||
// 请求方式
|
||||
reqMethod := c.Request.Method
|
||||
// 请求路由
|
||||
reqUri := c.Request.RequestURI
|
||||
// 状态码
|
||||
statusCode := c.Writer.Status()
|
||||
// 请求IP
|
||||
clientIP := common.GetClientIP(c)
|
||||
// 执行时间
|
||||
latencyTime := endTime.Sub(startTime)
|
||||
// 日志格式
|
||||
logData := map[string]interface{}{
|
||||
"statusCode": statusCode,
|
||||
"latencyTime": latencyTime,
|
||||
"clientIP": clientIP,
|
||||
"method": reqMethod,
|
||||
"uri": reqUri,
|
||||
}
|
||||
log.WithFields(logData).Info()
|
||||
|
||||
if c.Request.Method != "OPTIONS" && config.LoggerConfig.EnabledDB && statusCode != 404 {
|
||||
SetDBOperLog(c, clientIP, statusCode, reqUri, reqMethod, latencyTime, body, result, statusBus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetDBOperLog 写入操作日志表 fixme 该方法后续即将弃用
|
||||
func SetDBOperLog(c *gin.Context, clientIP string, statusCode int, reqUri string, reqMethod string, latencyTime time.Duration, body string, result string, status int) {
|
||||
|
||||
log := api.GetRequestLogger(c)
|
||||
l := make(map[string]interface{})
|
||||
l["_fullPath"] = c.FullPath()
|
||||
l["operUrl"] = reqUri
|
||||
l["operIp"] = clientIP
|
||||
l["operLocation"] = "" // pkg.GetLocation(clientIP, gaConfig.ExtConfig.AMap.Key)
|
||||
l["operName"] = user.GetUserName(c)
|
||||
l["requestMethod"] = reqMethod
|
||||
l["operParam"] = body
|
||||
l["operTime"] = time.Now()
|
||||
l["jsonResult"] = result
|
||||
l["latencyTime"] = latencyTime.String()
|
||||
l["statusCode"] = statusCode
|
||||
l["userAgent"] = c.Request.UserAgent()
|
||||
l["createBy"] = user.GetUserId(c)
|
||||
l["updateBy"] = user.GetUserId(c)
|
||||
if status == http.StatusOK {
|
||||
l["status"] = dto.OperaStatusEnabel
|
||||
} else {
|
||||
l["status"] = dto.OperaStatusDisable
|
||||
}
|
||||
q := sdk.Runtime.GetMemoryQueue(c.Request.Host)
|
||||
message, err := sdk.Runtime.GetStreamMessage("", global.OperateLog, l)
|
||||
if err != nil {
|
||||
log.Errorf("GetStreamMessage error, %s", err.Error())
|
||||
//日志报错错误,不中断请求
|
||||
} else {
|
||||
err = q.Append(message)
|
||||
if err != nil {
|
||||
log.Errorf("Append message error, %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
61
common/middleware/permission.go
Normal file
61
common/middleware/permission.go
Normal file
@ -0,0 +1,61 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/casbin/casbin/v2/util"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/api"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/response"
|
||||
)
|
||||
|
||||
// AuthCheckRole 权限检查中间件
|
||||
func AuthCheckRole() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
log := api.GetRequestLogger(c)
|
||||
data, _ := c.Get(jwtauth.JwtPayloadKey)
|
||||
v := data.(jwtauth.MapClaims)
|
||||
e := sdk.Runtime.GetCasbinKey(c.Request.Host)
|
||||
var res, casbinExclude bool
|
||||
var err error
|
||||
//检查权限
|
||||
if v["rolekey"] == "admin" {
|
||||
res = true
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
for _, i := range CasbinExclude {
|
||||
if util.KeyMatch2(c.Request.URL.Path, i.Url) && c.Request.Method == i.Method {
|
||||
casbinExclude = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if casbinExclude {
|
||||
log.Infof("Casbin exclusion, no validation method:%s path:%s", c.Request.Method, c.Request.URL.Path)
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
res, err = e.Enforce(v["rolekey"], c.Request.URL.Path, c.Request.Method)
|
||||
if err != nil {
|
||||
log.Errorf("AuthCheckRole error:%s method:%s path:%s", err, c.Request.Method, c.Request.URL.Path)
|
||||
response.Error(c, 500, err, "")
|
||||
return
|
||||
}
|
||||
|
||||
if res {
|
||||
log.Infof("isTrue: %v role: %s method: %s path: %s", res, v["rolekey"], c.Request.Method, c.Request.URL.Path)
|
||||
c.Next()
|
||||
} else {
|
||||
log.Warnf("isTrue: %v role: %s method: %s path: %s message: %s", res, v["rolekey"], c.Request.Method, c.Request.URL.Path, "当前request无权限,请管理员确认!")
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 403,
|
||||
"msg": "对不起,您没有该接口访问权限,请联系管理员",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
36
common/middleware/request_id.go
Normal file
36
common/middleware/request_id.go
Normal file
@ -0,0 +1,36 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// RequestId 自动增加requestId
|
||||
func RequestId(trafficKey string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if c.Request.Method == http.MethodOptions {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
requestId := c.GetHeader(trafficKey)
|
||||
if requestId == "" {
|
||||
requestId = c.GetHeader(strings.ToLower(trafficKey))
|
||||
}
|
||||
if requestId == "" {
|
||||
requestId = uuid.New().String()
|
||||
}
|
||||
c.Request.Header.Set(trafficKey, requestId)
|
||||
c.Set(trafficKey, requestId)
|
||||
c.Set(pkg.LoggerKey,
|
||||
logger.NewHelper(logger.DefaultLogger).
|
||||
WithFields(map[string]interface{}{
|
||||
trafficKey: requestId,
|
||||
}))
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
30
common/middleware/sentinel.go
Normal file
30
common/middleware/sentinel.go
Normal file
@ -0,0 +1,30 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/alibaba/sentinel-golang/core/system"
|
||||
sentinel "github.com/alibaba/sentinel-golang/pkg/adapters/gin"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
)
|
||||
|
||||
// Sentinel 限流
|
||||
func Sentinel() gin.HandlerFunc {
|
||||
if _, err := system.LoadRules([]*system.Rule{
|
||||
{
|
||||
MetricType: system.InboundQPS,
|
||||
TriggerCount: 200,
|
||||
Strategy: system.BBR,
|
||||
},
|
||||
}); err != nil {
|
||||
log.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
return sentinel.SentinelMiddleware(
|
||||
sentinel.WithBlockFallback(func(ctx *gin.Context) {
|
||||
ctx.AbortWithStatusJSON(200, map[string]interface{}{
|
||||
"msg": "too many request; the quota used up!",
|
||||
"code": 500,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
43
common/middleware/settings.go
Normal file
43
common/middleware/settings.go
Normal file
@ -0,0 +1,43 @@
|
||||
package middleware
|
||||
|
||||
type UrlInfo struct {
|
||||
Url string
|
||||
Method string
|
||||
}
|
||||
|
||||
// CasbinExclude casbin 排除的路由列表
|
||||
var CasbinExclude = []UrlInfo{
|
||||
{Url: "/api/v1/dict/type-option-select", Method: "GET"},
|
||||
{Url: "/api/v1/dict-data/option-select", Method: "GET"},
|
||||
{Url: "/api/v1/deptTree", Method: "GET"},
|
||||
{Url: "/api/v1/db/tables/page", Method: "GET"},
|
||||
{Url: "/api/v1/db/columns/page", Method: "GET"},
|
||||
{Url: "/api/v1/gen/toproject/:tableId", Method: "GET"},
|
||||
{Url: "/api/v1/gen/todb/:tableId", Method: "GET"},
|
||||
{Url: "/api/v1/gen/tabletree", Method: "GET"},
|
||||
{Url: "/api/v1/gen/preview/:tableId", Method: "GET"},
|
||||
{Url: "/api/v1/gen/apitofile/:tableId", Method: "GET"},
|
||||
{Url: "/api/v1/getCaptcha", Method: "GET"},
|
||||
{Url: "/api/v1/getinfo", Method: "GET"},
|
||||
{Url: "/api/v1/menuTreeselect", Method: "GET"},
|
||||
{Url: "/api/v1/menurole", Method: "GET"},
|
||||
{Url: "/api/v1/menuids", Method: "GET"},
|
||||
{Url: "/api/v1/roleMenuTreeselect/:roleId", Method: "GET"},
|
||||
{Url: "/api/v1/roleDeptTreeselect/:roleId", Method: "GET"},
|
||||
{Url: "/api/v1/refresh_token", Method: "GET"},
|
||||
{Url: "/api/v1/configKey/:configKey", Method: "GET"},
|
||||
{Url: "/api/v1/app-config", Method: "GET"},
|
||||
{Url: "/api/v1/user/profile", Method: "GET"},
|
||||
{Url: "/info", Method: "GET"},
|
||||
{Url: "/api/v1/login", Method: "POST"},
|
||||
{Url: "/api/v1/logout", Method: "POST"},
|
||||
{Url: "/api/v1/user/avatar", Method: "POST"},
|
||||
{Url: "/api/v1/user/pwd", Method: "PUT"},
|
||||
{Url: "/api/v1/metrics", Method: "GET"},
|
||||
{Url: "/api/v1/health", Method: "GET"},
|
||||
{Url: "/", Method: "GET"},
|
||||
{Url: "/api/v1/server-monitor", Method: "GET"},
|
||||
{Url: "/api/v1/public/uploadFile", Method: "POST"},
|
||||
{Url: "/api/v1/user/pwd/set", Method: "PUT"},
|
||||
{Url: "/api/v1/sys-user", Method: "PUT"},
|
||||
}
|
||||
27
common/middleware/trace.go
Normal file
27
common/middleware/trace.go
Normal file
@ -0,0 +1,27 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
// Trace 链路追踪
|
||||
func Trace() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
var sp opentracing.Span
|
||||
opName := ctx.Request.URL.Path
|
||||
// Attempt to join a trace by getting trace context from the headers.
|
||||
wireContext, err := opentracing.GlobalTracer().Extract(
|
||||
opentracing.TextMap,
|
||||
opentracing.HTTPHeadersCarrier(ctx.Request.Header))
|
||||
if err != nil {
|
||||
// If for whatever reason we can't join, go ahead and start a new root span.
|
||||
sp = opentracing.StartSpan(opName)
|
||||
} else {
|
||||
sp = opentracing.StartSpan(opName, opentracing.ChildOf(wireContext))
|
||||
}
|
||||
ctx.Set("traceSpan", sp)
|
||||
ctx.Next()
|
||||
sp.Finish()
|
||||
}
|
||||
}
|
||||
32
common/models/by.go
Normal file
32
common/models/by.go
Normal file
@ -0,0 +1,32 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ControlBy struct {
|
||||
CreateBy int `json:"createBy" gorm:"index;comment:创建者"`
|
||||
UpdateBy int `json:"updateBy" gorm:"index;comment:更新者"`
|
||||
}
|
||||
|
||||
// SetCreateBy 设置创建人id
|
||||
func (e *ControlBy) SetCreateBy(createBy int) {
|
||||
e.CreateBy = createBy
|
||||
}
|
||||
|
||||
// SetUpdateBy 设置修改人id
|
||||
func (e *ControlBy) SetUpdateBy(updateBy int) {
|
||||
e.UpdateBy = updateBy
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
Id int `json:"id" gorm:"primaryKey;autoIncrement;comment:主键编码"`
|
||||
}
|
||||
|
||||
type ModelTime struct {
|
||||
CreatedAt time.Time `json:"createdAt" gorm:"comment:创建时间"`
|
||||
UpdatedAt time.Time `json:"updatedAt" gorm:"comment:最后更新时间"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index;comment:删除时间"`
|
||||
}
|
||||
11
common/models/menu.go
Normal file
11
common/models/menu.go
Normal file
@ -0,0 +1,11 @@
|
||||
package models
|
||||
|
||||
// Menu 菜单中的类型枚举值
|
||||
const (
|
||||
// Directory 目录
|
||||
Directory string = "M"
|
||||
// Menu 菜单
|
||||
Menu string = "C"
|
||||
// Button 按钮
|
||||
Button string = "F"
|
||||
)
|
||||
12
common/models/migrate.go
Normal file
12
common/models/migrate.go
Normal file
@ -0,0 +1,12 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
type Migration struct {
|
||||
Version string `gorm:"primaryKey"`
|
||||
ApplyTime time.Time `gorm:"autoCreateTime"`
|
||||
}
|
||||
|
||||
func (Migration) TableName() string {
|
||||
return "sys_migration"
|
||||
}
|
||||
30
common/models/response.go
Normal file
30
common/models/response.go
Normal file
@ -0,0 +1,30 @@
|
||||
package models
|
||||
|
||||
type Response struct {
|
||||
// 代码
|
||||
Code int `json:"code" example:"200"`
|
||||
// 数据集
|
||||
Data interface{} `json:"data"`
|
||||
// 消息
|
||||
Msg string `json:"msg"`
|
||||
RequestId string `json:"requestId"`
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
List interface{} `json:"list"`
|
||||
Count int `json:"count"`
|
||||
PageIndex int `json:"pageIndex"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
|
||||
// ReturnOK 正常返回
|
||||
func (res *Response) ReturnOK() *Response {
|
||||
res.Code = 200
|
||||
return res
|
||||
}
|
||||
|
||||
// ReturnError 错误返回
|
||||
func (res *Response) ReturnError(code int) *Response {
|
||||
res.Code = code
|
||||
return res
|
||||
}
|
||||
11
common/models/type.go
Normal file
11
common/models/type.go
Normal file
@ -0,0 +1,11 @@
|
||||
package models
|
||||
|
||||
import "gorm.io/gorm/schema"
|
||||
|
||||
type ActiveRecord interface {
|
||||
schema.Tabler
|
||||
SetCreateBy(createBy int)
|
||||
SetUpdateBy(updateBy int)
|
||||
Generate() ActiveRecord
|
||||
GetId() interface{}
|
||||
}
|
||||
42
common/models/user.go
Normal file
42
common/models/user.go
Normal file
@ -0,0 +1,42 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
)
|
||||
|
||||
// BaseUser 密码登录基础用户
|
||||
type BaseUser struct {
|
||||
Username string `json:"username" gorm:"type:varchar(100);comment:用户名"`
|
||||
Salt string `json:"-" gorm:"type:varchar(255);comment:加盐;<-"`
|
||||
PasswordHash string `json:"-" gorm:"type:varchar(128);comment:密码hash;<-"`
|
||||
Password string `json:"password" gorm:"-"`
|
||||
}
|
||||
|
||||
// SetPassword 设置密码
|
||||
func (u *BaseUser) SetPassword(value string) {
|
||||
u.Password = value
|
||||
u.generateSalt()
|
||||
u.PasswordHash = u.GetPasswordHash()
|
||||
}
|
||||
|
||||
// GetPasswordHash 获取密码hash
|
||||
func (u *BaseUser) GetPasswordHash() string {
|
||||
passwordHash, err := pkg.SetPassword(u.Password, u.Salt)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return passwordHash
|
||||
}
|
||||
|
||||
// generateSalt 生成加盐值
|
||||
func (u *BaseUser) generateSalt() {
|
||||
u.Salt = pkg.GenerateRandomKey16()
|
||||
}
|
||||
|
||||
// Verify 验证密码
|
||||
func (u *BaseUser) Verify(db *gorm.DB, tableName string) bool {
|
||||
db.Table(tableName).Where("username = ?", u.Username).First(u)
|
||||
return u.GetPasswordHash() == u.PasswordHash
|
||||
}
|
||||
105
common/response/binding.go
Normal file
105
common/response/binding.go
Normal file
@ -0,0 +1,105 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
_ uint8 = iota
|
||||
json
|
||||
xml
|
||||
yaml
|
||||
form
|
||||
query
|
||||
)
|
||||
|
||||
//var constructor = &bindConstructor{}
|
||||
|
||||
type bindConstructor struct {
|
||||
cache map[string][]uint8
|
||||
mux sync.Mutex
|
||||
}
|
||||
|
||||
func (e *bindConstructor) GetBindingForGin(d interface{}) []binding.Binding {
|
||||
bs := e.getBinding(reflect.TypeOf(d).String())
|
||||
if bs == nil {
|
||||
//重新构建
|
||||
bs = e.resolve(d)
|
||||
}
|
||||
gbs := make([]binding.Binding, len(bs))
|
||||
for i, b := range bs {
|
||||
switch b {
|
||||
case json:
|
||||
gbs[i] = binding.JSON
|
||||
case xml:
|
||||
gbs[i] = binding.XML
|
||||
case yaml:
|
||||
gbs[i] = binding.YAML
|
||||
case form:
|
||||
gbs[i] = binding.Form
|
||||
case query:
|
||||
gbs[i] = binding.Query
|
||||
default:
|
||||
gbs[i] = nil
|
||||
}
|
||||
}
|
||||
return gbs
|
||||
}
|
||||
|
||||
func (e *bindConstructor) resolve(d interface{}) []uint8 {
|
||||
bs := make([]uint8, 0)
|
||||
qType := reflect.TypeOf(d).Elem()
|
||||
var tag reflect.StructTag
|
||||
var ok bool
|
||||
fmt.Println(qType.Kind())
|
||||
for i := 0; i < qType.NumField(); i++ {
|
||||
tag = qType.Field(i).Tag
|
||||
if _, ok = tag.Lookup("json"); ok {
|
||||
bs = append(bs, json)
|
||||
}
|
||||
if _, ok = tag.Lookup("xml"); ok {
|
||||
bs = append(bs, xml)
|
||||
}
|
||||
if _, ok = tag.Lookup("yaml"); ok {
|
||||
bs = append(bs, yaml)
|
||||
}
|
||||
if _, ok = tag.Lookup("form"); ok {
|
||||
bs = append(bs, form)
|
||||
}
|
||||
if _, ok = tag.Lookup("query"); ok {
|
||||
bs = append(bs, query)
|
||||
}
|
||||
if _, ok = tag.Lookup("uri"); ok {
|
||||
bs = append(bs, 0)
|
||||
}
|
||||
if t, ok := tag.Lookup("binding"); ok && strings.Index(t, "dive") > -1 {
|
||||
qValue := reflect.ValueOf(d)
|
||||
bs = append(bs, e.resolve(qValue.Field(i))...)
|
||||
continue
|
||||
}
|
||||
if t, ok := tag.Lookup("validate"); ok && strings.Index(t, "dive") > -1 {
|
||||
qValue := reflect.ValueOf(d)
|
||||
bs = append(bs, e.resolve(qValue.Field(i))...)
|
||||
}
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (e *bindConstructor) getBinding(name string) []uint8 {
|
||||
e.mux.Lock()
|
||||
defer e.mux.Unlock()
|
||||
return e.cache[name]
|
||||
}
|
||||
|
||||
func (e *bindConstructor) setBinding(name string, bs []uint8) {
|
||||
e.mux.Lock()
|
||||
defer e.mux.Unlock()
|
||||
if e.cache == nil {
|
||||
e.cache = make(map[string][]uint8)
|
||||
}
|
||||
e.cache[name] = bs
|
||||
}
|
||||
25
common/service/service.go
Normal file
25
common/service/service.go
Normal file
@ -0,0 +1,25 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/logger"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
Orm *gorm.DB
|
||||
Msg string
|
||||
MsgID string
|
||||
Log *logger.Helper
|
||||
Error error
|
||||
}
|
||||
|
||||
func (db *Service) AddError(err error) error {
|
||||
if db.Error == nil {
|
||||
db.Error = err
|
||||
} else if err != nil {
|
||||
db.Error = fmt.Errorf("%v; %w", db.Error, err)
|
||||
}
|
||||
return db.Error
|
||||
}
|
||||
52
common/storage/initialize.go
Normal file
52
common/storage/initialize.go
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* @Author: lwnmengjing
|
||||
* @Date: 2021/6/10 3:39 下午
|
||||
* @Last Modified by: lwnmengjing
|
||||
* @Last Modified time: 2021/6/10 3:39 下午
|
||||
*/
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/config"
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg/captcha"
|
||||
)
|
||||
|
||||
// Setup 配置storage组件
|
||||
func Setup() {
|
||||
//4. 设置缓存
|
||||
cacheAdapter, err := config.CacheConfig.Setup()
|
||||
if err != nil {
|
||||
log.Fatalf("cache setup error, %s\n", err.Error())
|
||||
}
|
||||
sdk.Runtime.SetCacheAdapter(cacheAdapter)
|
||||
//5. 设置验证码store
|
||||
captcha.SetStore(captcha.NewCacheStore(cacheAdapter, 600))
|
||||
|
||||
//6. 设置队列
|
||||
if !config.QueueConfig.Empty() {
|
||||
if q := sdk.Runtime.GetQueueAdapter(); q != nil {
|
||||
q.Shutdown()
|
||||
}
|
||||
queueAdapter, err := config.QueueConfig.Setup()
|
||||
if err != nil {
|
||||
log.Fatalf("queue setup error, %s\n", err.Error())
|
||||
}
|
||||
sdk.Runtime.SetQueueAdapter(queueAdapter)
|
||||
defer func() {
|
||||
go queueAdapter.Run()
|
||||
}()
|
||||
}
|
||||
|
||||
//7. 设置分布式锁
|
||||
if !config.LockerConfig.Empty() {
|
||||
lockerAdapter, err := config.LockerConfig.Setup()
|
||||
if err != nil {
|
||||
log.Fatalf("locker setup error, %s\n", err.Error())
|
||||
}
|
||||
sdk.Runtime.SetLockerAdapter(lockerAdapter)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user