Home
Softono
superAdminApi20

superAdminApi20

Open source Apache-2.0 Go
14
Stars
3
Forks
0
Issues
1
Watchers
1 year
Last Commit

About superAdminApi20

superAdmin 开箱即用的后台框架,支持权限管理、无限极分类、平滑重启、菜单管理、消息队列(即时和延迟)、任务调度、session、大文件上传等功能

Platforms

Web Self-hosted

Languages

Go

Links

superAdmin

开箱即用的后台框架

中文文档

相关博客

在线demo

https://www.peterdemo.net

账号:test

密码:Aa123456

前端仓库

https://github.com/PeterYangs/superAdminPage20

环境要求

redis

mysql

开发模式

go run main.go

编译&部署

编译二进制文件

go build main.go

直接运行

./main

或者

./main start

后台运行

./main start -d

安全停止

./main stop

运行内置命令

./main artisan

Quick start

controller

package controller

import (
    "github.com/PeterYangs/superAdminCore/v2/contextPlus"
    "github.com/PeterYangs/superAdminCore/v2/response"
)

func Index(c *contextPlus.Context) *response.Response {

    return response.Resp().Api(1, "success", "index")
}

route

routes/routes.go

func Routes(_r route.Group) {

    _r.Registered(route.GET, "/index", controller.Index).Bind()

    _r.Group("/login", func (_login route.Group) {

        _login.Registered(route.POST, "/login", login.Login, loginLimiter.LoginLimiter).Bind()

        _login.Registered(route.POST, "/logout", login.Logout).Bind()

    })
}

session

func Session(c *contextPlus.Context) *response.Response {

    c.Session.Set("key", "value")

    c.Session.Get("key")

    return nil
}

全局中间件

middleware/global.go

package middleware

import (
    "github.com/PeterYangs/superAdminCore/v2/contextPlus"
    "github.com/PeterYangs/superAdminCore/v2/middleware/session"
    "superadmin/middleware/accessLog"
)

func Load() []contextPlus.HandlerFunc {

    return []contextPlus.HandlerFunc{

        session.StartSession,
        accessLog.AccessLog,
    }
}

验证码

获取验证码

func Captcha(c *contextPlus.Context) *response.Response {

    b := c.GetCaptcha()

    c.Header("content-type", "image/png")

    return response.Resp().Byte(b)
}

检查验证码

func CheckCaptcha(c *contextPlus.Context) *response.Response {

    code := c.Query("code")

    bool:= c.CheckCaptcha(code)

    return response.Resp().Json(gin.H{"bool":code})

}

参数验证

package regex

import (
    "gin-web/contextPlus"
    "github.com/gin-gonic/gin"
)

// Regex 参数规则验证示例,路由为 /regex/:name ,请求为 /regex/1sds?test[]=1&test[]=2,regex标记只支持string和[]string两个类型
func Regex(c *contextPlus.Context) *response.Response {

    type regex struct {
        Test []string `form:"test[]" json:"test" regex:"[0-9a-z/]+"`
        Name string   `uri:"name" json:"name" regex:"[0-9a-z]+"`
    }

    var t regex

    err := c.ShouldBindPlus(&t)

    if err != nil {

        return response.Resp().Json(gin.H{"code": 2, "msg": err.Error()})

    }

    return response.Resp().Json(gin.H{"code": 1, "msg": "hello world"})
}

数据库迁移


[root@localhost ~]# ./main artisan
Use the arrow keys to navigate: ↓ ↑ → ←
? 选择类型:
  > 数据库迁移
    数据填充
    生成key
    生成任务类

迁移文件

package migrate_2019_08_12_055619_create_admin_table

import "gin-web/migrate"

func Up() {

    migrate.Create("admin", func(createMigrate *migrate.Migrate) {

        createMigrate.Name = "migrate_2019_08_12_055619_create_admin_table"

        //主键
        createMigrate.BigIncrements("id")

        //int
        createMigrate.Integer("user_id").Unsigned().Nullable().Default(0).Unique().Comment("用户id")

        //varchar
        createMigrate.String("title", 255).Default("").Comment("标题")

        //text
        createMigrate.Text("content").Default(migrate.Null).Comment("内容")

        //索引
        createMigrate.Unique("user_id", "title")

    })

}

func Down() {

    migrate.DropIfExists("admin")

}

限流器

package loginLimiter

import (
    "gin-web/component/limiter"
    "gin-web/contextPlus"
    "golang.org/x/time/rate"
    "time"
)

func LoginLimiter(c *contextPlus.Context) {

    //每秒生成一个令牌,桶的大小是10,第三个参数是自定义key,根据自定义的key寻找限流器(默认是每1分钟清理一次过期的限流器)
    if !limiter.NewLimiter(rate.Every(1*time.Second), 10, c.ClientIP()).Allow() {

        c.String(500, "访问频率过高")

        c.Abort()

    }

}

分布式锁

非阻塞

func Index(c *contextPlus.Context) *response.Response {

    //申请一个锁,过期时间是10秒
    lock := redis.GetClient().Lock("lock", 10*time.Second)

    //释放锁
    defer lock.Release()

    //是否拿到锁
    if lock.Get() {

        return response.Resp().Json(gin.H{"res": true})

    }

    return response.Resp().Json(gin.H{"res": false})

}

阻塞

func Index(c *contextPlus.Context) *response.Response {

    //申请一个锁,过期时间是10秒
    lock := redis.GetClient().Lock("lock", 10*time.Second)

    defer lock.Release()

    //是否拿到锁
    if lock.Block(time.Second * 3) {

        time.Sleep(4 * time.Second)

        return response.Resp().Json(gin.H{"res": true})
    }

    return response.Resp().Json(gin.H{"res": false})

}

消息队列

生成任务类

[root@localhost superAdminApi20]# ./main artisan
Use the arrow keys to navigate: ↓ ↑ → ←
? 选择类型:
    数据库迁移
    数据填充
    生成key
  > 生成任务类

任务类

package access

import (
    "gin-web/database"
    "gin-web/model"
    "gin-web/task"
)

type TaskAccess struct {
    task.BaseTask
    Parameters *Parameter
}

type Parameter struct {
    task.Parameter
    Ip      string
    Url     string
    Params  string
    AdminId float64
}

func NewTask(ip string, url string, params string, adminId float64) *TaskAccess {

    return &TaskAccess{

        BaseTask: task.BaseTask{
            TaskName: "access",
        },
        Parameters: &Parameter{
            Ip:      ip,
            Url:     url,
            Params:  params,
            AdminId: adminId,
        },
    }
}

func (t *TaskAccess) Run() error {

    database.GetDb().Create(&model.Access{
        Ip:      t.Parameters.Ip,
        Url:     t.Parameters.Url,
        Params:  t.Parameters.Params,
        AdminId: t.Parameters.AdminId,
    })

    return nil

}

func (t *TaskAccess) BindParameters(p map[string]interface{}) {

    t.BaseTask.Bind(t.Parameters, p)

}

即时任务

package controller

import (
    "gin-web/contextPlus"
    "gin-web/queue"
    "gin-web/response"
    "gin-web/task/email"
    "gin-web/task/sms"
)

func Task(c *contextPlus.Context) *response.Response {

    queue.Dispatch(email.NewTask("[email protected]", "title", "content")).Queue("low").Run()

    return response.Resp().Api(1, "123", "")

}

延迟队列

package controller

import (
    "gin-web/contextPlus"
    "gin-web/queue"
    "gin-web/response"
    "gin-web/task/email"
    "gin-web/task/sms"
    "time"
)

func Task(c *contextPlus.Context) *response.Response {

    queue.Dispatch(email.NewTask("[email protected]", "title", "content")).Queue("low").Delay(100 * time.Second).Run()

    return response.Resp().Api(1, "123", "")

}

重试次数

package controller

import (
    "gin-web/contextPlus"
    "gin-web/queue"
    "gin-web/response"
    "gin-web/task/email"
    "gin-web/task/sms"
)

func Task(c *contextPlus.Context) *response.Response {

    queue.Dispatch(email.NewTask("[email protected]", "title", "content")).SetTryTime(3).Run()

    return response.Resp().Api(1, "123", "")

}

任务调度

crontab/crontabs.go

package crontab

import "fmt"

func Crontab(c *crontab) {

    c.newSchedule().everyHour().function(func() {

        fmt.Println("每小时")

    })

    c.newSchedule().hourlyAt(16).everyMinute().function(func() {

        fmt.Println("每个16点的每分钟")

    })

    c.newSchedule().minuteAt(18).function(func() {

        fmt.Println("每小时的第18分钟")

    })

    c.newSchedule().everyMinute().function(func() {

        //panic("模拟报错")

        fmt.Println("每分钟")

    })

    c.newSchedule().everyMinuteAt(2).function(func() {

        fmt.Println("每2分钟")

    })

    c.newSchedule().everyDay().hourlyAt(16).minuteAt(36).function(func() {

        fmt.Println("每天16点36分")

    })

    c.newSchedule().dayAt(23).hourlyAt(16).minuteAt(50).function(func() {

        fmt.Println("23号16点50分")

    })

    c.newSchedule().dayAt(24).hourBetween(8, 10).function(func() {

        fmt.Println("24号8点-10点")

    })

    c.newSchedule().hourBetween(8, 9).everyMinute().function(func() {

        fmt.Println("每天8点-9点每分钟")

    })

    c.newSchedule().dayBetween(22, 24).everyHour().everyMinute().function(func() {

        fmt.Println("22号-24号每分钟")

    })

}

命令行

需要实现

type Artisan interface {
    ArtisanRun()
    GetName() string
}

一个例子

package demo

import "github.com/PeterYangs/superAdminCore/v2/component/logs"

type Demo struct {
}

func (d Demo) GetName() string {

    return "demo"
}

func (d Demo) ArtisanRun() {

    logs.NewLogs().Debug("demo")
}

运行

./main artisan

缓存

缓存支持两种驱动fileredis,在.env文件中设置CACHE_DRIVER=redis

package test

import (
    "fmt"
    "github.com/PeterYangs/superAdminCore/v2/cache"
    "github.com/PeterYangs/superAdminCore/v2/contextPlus"
    "github.com/PeterYangs/superAdminCore/v2/response"
)

func Cache(c *contextPlus.Context) *response.Response {

    cache.Cache().Put("nice", "0", 0)

    fmt.Println(cache.Cache().Get("nice"))

    return response.Resp().Api(1, "success", "")
}

管理员登录模式

在.env文件中设置

#登录模式,single(单点登录),multi(多点登录)
LOGIN_MODE=single|multi