1、go接入gin(完成)
2、go接入gorm(完成) 3、环境yaml配置(完成) 4、多库支持(完成) 5、接入日志(完成) 6、接入redis(完成) 7、自定义路由(完成) 8、接入jwt(完成)
This commit is contained in:
parent
6b21987033
commit
1a8707eee6
|
@ -0,0 +1,21 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseDO struct {
|
||||||
|
// 自增ID
|
||||||
|
ID uint `gorm:"column:id;type:bigint(20);not null;primary_key;AUTO_INCREMENT" json:"id" form:"id"`
|
||||||
|
// 创建时间
|
||||||
|
CreatedAt time.Time
|
||||||
|
// 更新时间
|
||||||
|
UpdatedAt time.Time
|
||||||
|
// 删除时间
|
||||||
|
DeletedAt *time.Time `sql:"index"`
|
||||||
|
// 扩展字段
|
||||||
|
// 创建者
|
||||||
|
CreatedBy string
|
||||||
|
// 删除者
|
||||||
|
UpdatedBy string
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
type SimplePage struct {
|
||||||
|
Page uint `json:"page"`
|
||||||
|
PageSize uint `json:"pageSize"`
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
type SimplePageData struct {
|
||||||
|
total uint `json:"total"`
|
||||||
|
data interface{} `json:"data"`
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 配置文件
|
||||||
|
type AppConfig struct {
|
||||||
|
// 环境
|
||||||
|
Env string `yaml:"env"`
|
||||||
|
// 运行模式
|
||||||
|
Mode string `yaml:"mode"`
|
||||||
|
// 应用名称
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
}
|
||||||
|
type ServerConfig struct {
|
||||||
|
// 服务端口
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
}
|
||||||
|
type LogConfig struct {
|
||||||
|
// 日志文件配置
|
||||||
|
File LogFileConfig `yaml:"file"`
|
||||||
|
// 日志级别(debug, info, warn, error)
|
||||||
|
Level string `yaml:"level"`
|
||||||
|
}
|
||||||
|
type LogFileConfig struct {
|
||||||
|
// 日志路径
|
||||||
|
Path string `yaml:"path"`
|
||||||
|
// 日志名称
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RedisConfig struct {
|
||||||
|
// 连接地址
|
||||||
|
Addr string `yaml:"addr"`
|
||||||
|
// 密码
|
||||||
|
Passwd string `yaml:"passwd"`
|
||||||
|
// 库索引
|
||||||
|
Database int `yaml:"database"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DataBaseConfig struct {
|
||||||
|
// 数据库方言
|
||||||
|
Dialect string `yaml:"dialect"`
|
||||||
|
// 数据源
|
||||||
|
DataSources DataSourceConfig `yaml:"datasources"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DataSourceConfig struct {
|
||||||
|
MySQL DataSourceMySQLConfig `yaml:"mysql"`
|
||||||
|
SQLite3 DataSourceSQLite3Config `yaml:"sqlite3"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DataSourceMySQLConfig struct {
|
||||||
|
// 连接地址
|
||||||
|
Addr string `yaml:"addr"`
|
||||||
|
// 用户名
|
||||||
|
Username string `yaml:"username"`
|
||||||
|
// 密码
|
||||||
|
Password string `yaml:"password"`
|
||||||
|
// 数据库名称
|
||||||
|
Database string `yaml:"database"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DataSourceSQLite3Config struct {
|
||||||
|
// 数据库文件
|
||||||
|
DBFile string `yaml:"dbfile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JWTConfig struct {
|
||||||
|
// 加密盐
|
||||||
|
Secret string `yaml:"secret"`
|
||||||
|
// 过期时间
|
||||||
|
Expiry int `yaml:"expiry"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApplicationProperties struct {
|
||||||
|
// 应用配置
|
||||||
|
App AppConfig `yaml:"app"`
|
||||||
|
// Gin服务配置
|
||||||
|
Server ServerConfig `yaml:"server"`
|
||||||
|
// 日志配置
|
||||||
|
Log LogConfig `yaml:"log"`
|
||||||
|
// redis配置
|
||||||
|
Redis RedisConfig `yaml:"redis"`
|
||||||
|
// 数据库配置
|
||||||
|
DataBase DataBaseConfig `yaml:"database"`
|
||||||
|
// jwt配置
|
||||||
|
Jwt JWTConfig `yaml:"jwt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据路径读取yaml文件
|
||||||
|
func readYaml(path string) ApplicationProperties {
|
||||||
|
var result ApplicationProperties
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
Logger.Error("File reading error, application.yml not exist!" + err.Error())
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = yaml.Unmarshal(data, &result)
|
||||||
|
if err != nil {
|
||||||
|
Logger.Errorf("cannot unmarshal data: %v", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据环境配置取配置明细
|
||||||
|
func (v *ApplicationProperties) Init() {
|
||||||
|
result := readYaml(fmt.Sprintf("%s/application.yml", GetCurrentPath()))
|
||||||
|
// 判断环境
|
||||||
|
if strings.Compare(result.App.Env, "dev") == 0 {
|
||||||
|
AppProps = readYaml(fmt.Sprintf("%s/application-%s.yml", GetCurrentPath(), "dev"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前路径,比如:d:/abc
|
||||||
|
func GetCurrentPath() string {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
Logger.Error(err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return strings.Replace(dir, "\\", "/", -1)
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
// 应用配置
|
||||||
|
var AppProps ApplicationProperties
|
||||||
|
|
||||||
|
// jwt拦截器
|
||||||
|
var JwtHandle gin.HandlerFunc
|
|
@ -0,0 +1,59 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
_ "github.com/jinzhu/gorm/dialects/mssql"
|
||||||
|
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||||
|
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||||
|
//_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||||
|
"lingye-gin/src/entity"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SqlExcutor *gorm.DB
|
||||||
|
|
||||||
|
type DataSourcePool struct{}
|
||||||
|
|
||||||
|
func (v DataSourcePool) Connect() DataSourcePool {
|
||||||
|
if strings.Compare(AppProps.DataBase.Dialect, "") == 0 {
|
||||||
|
panic("yaml file dialect property is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if strings.Compare(AppProps.DataBase.Dialect, "mysql") == 0 {
|
||||||
|
connectStr := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local",
|
||||||
|
AppProps.DataBase.DataSources.MySQL.Username,
|
||||||
|
AppProps.DataBase.DataSources.MySQL.Password,
|
||||||
|
AppProps.DataBase.DataSources.MySQL.Addr,
|
||||||
|
AppProps.DataBase.DataSources.MySQL.Database)
|
||||||
|
SqlExcutor, err = gorm.Open(AppProps.DataBase.Dialect, connectStr)
|
||||||
|
// 创建表时添加表后缀
|
||||||
|
if SqlExcutor != nil {
|
||||||
|
SqlExcutor.Set("gorm:table_options", "ENGINE=InnoDB")
|
||||||
|
}
|
||||||
|
} else if strings.Compare(AppProps.DataBase.Dialect, "sqlite3") == 0 {
|
||||||
|
// windows上会报'cgo: exec gcc: exec'异常, 注掉吧
|
||||||
|
// SqlExcutor, err = gorm.Open(AppProps.DataBase.Dialect, AppProps.DataBase.DataSources.SQLite3.DBFile)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
Logger.Error("db connect error: " + err.Error())
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if SqlExcutor != nil {
|
||||||
|
// 全局禁用表名复数
|
||||||
|
// 如果设置为true,`User`的默认表名为`user`,使用`TableName`设置的表名不受影响
|
||||||
|
SqlExcutor.SingularTable(true)
|
||||||
|
// 设置为true之后控制台会输出对应的SQL语句
|
||||||
|
SqlExcutor.LogMode(true)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一在这里注册数据表
|
||||||
|
func (DataSourcePool) LoadEntity() {
|
||||||
|
// 自动迁移模式
|
||||||
|
if SqlExcutor != nil {
|
||||||
|
SqlExcutor.AutoMigrate(&entity.User{})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
||||||
|
"github.com/rifflock/lfshook"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 实例化
|
||||||
|
var Logger = logrus.New()
|
||||||
|
|
||||||
|
// 日志记录到文件
|
||||||
|
func LoggerToFile() gin.HandlerFunc {
|
||||||
|
logFilePath := AppProps.Log.File.Path
|
||||||
|
logFileName := AppProps.Log.File.Name
|
||||||
|
// 日志文件
|
||||||
|
fileName := path.Join(logFilePath, logFileName)
|
||||||
|
|
||||||
|
//禁止logrus的输出
|
||||||
|
src, err := os.OpenFile(os.DevNull, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("err: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置输出
|
||||||
|
Logger.Out = src
|
||||||
|
// 设置日志级别
|
||||||
|
if strings.Compare(AppProps.Log.Level, "debug") == 0 {
|
||||||
|
Logger.SetLevel(logrus.DebugLevel)
|
||||||
|
} else if strings.Compare(AppProps.Log.Level, "info") == 0 {
|
||||||
|
Logger.SetLevel(logrus.InfoLevel)
|
||||||
|
} else if strings.Compare(AppProps.Log.Level, "warn") == 0 {
|
||||||
|
Logger.SetLevel(logrus.WarnLevel)
|
||||||
|
} else if strings.Compare(AppProps.Log.Level, "error") == 0 {
|
||||||
|
Logger.SetLevel(logrus.ErrorLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置 rotatelogs
|
||||||
|
logWriter, err := rotatelogs.New(
|
||||||
|
// 分割后的文件名称
|
||||||
|
fileName+".%Y%m%d.log",
|
||||||
|
// 生成软链,指向最新日志文件
|
||||||
|
rotatelogs.WithLinkName(fileName),
|
||||||
|
// 设置最大保存时间(7天)
|
||||||
|
rotatelogs.WithMaxAge(7*24*time.Hour),
|
||||||
|
// 设置日志切割时间间隔(1天)
|
||||||
|
rotatelogs.WithRotationTime(24*time.Hour),
|
||||||
|
)
|
||||||
|
|
||||||
|
writeMap := lfshook.WriterMap{
|
||||||
|
logrus.InfoLevel: logWriter,
|
||||||
|
logrus.FatalLevel: logWriter,
|
||||||
|
logrus.DebugLevel: logWriter,
|
||||||
|
logrus.WarnLevel: logWriter,
|
||||||
|
logrus.ErrorLevel: logWriter,
|
||||||
|
logrus.PanicLevel: logWriter,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增钩子
|
||||||
|
Logger.AddHook(lfshook.NewHook(writeMap, &logrus.JSONFormatter{
|
||||||
|
// 这个日期是真的牛皮, yyyy-MM-dd hh:mm:ss它不香吗
|
||||||
|
TimestampFormat: "2006-01-02 15:04:05",
|
||||||
|
}))
|
||||||
|
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// 开始时间
|
||||||
|
startTime := time.Now()
|
||||||
|
// 处理请求
|
||||||
|
c.Next()
|
||||||
|
// 结束时间
|
||||||
|
endTime := time.Now()
|
||||||
|
// 执行时间
|
||||||
|
latencyTime := endTime.Sub(startTime)
|
||||||
|
// 请求方式
|
||||||
|
reqMethod := c.Request.Method
|
||||||
|
// 请求路由
|
||||||
|
reqUri := c.Request.RequestURI
|
||||||
|
// 状态码
|
||||||
|
statusCode := c.Writer.Status()
|
||||||
|
// 请求IP
|
||||||
|
clientIP := c.ClientIP()
|
||||||
|
// 日志格式
|
||||||
|
Logger.WithFields(logrus.Fields{
|
||||||
|
"status_code": statusCode,
|
||||||
|
"latency_time": latencyTime,
|
||||||
|
"client_ip": clientIP,
|
||||||
|
"req_method": reqMethod,
|
||||||
|
"req_uri": reqUri,
|
||||||
|
}).Info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日志记录到 MongoDB
|
||||||
|
func LoggerToMongo() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日志记录到 ES
|
||||||
|
func LoggerToES() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日志记录到 MQ
|
||||||
|
func LoggerToMQ() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package dao
|
||||||
|
|
||||||
|
import (
|
||||||
|
"lingye-gin/src/config"
|
||||||
|
"lingye-gin/src/entity"
|
||||||
|
"lingye-gin/src/service/dto"
|
||||||
|
"lingye-gin/src/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserDAO struct{}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
func (UserDAO) Insert(resource *entity.User) {
|
||||||
|
config.SqlExcutor.Create(resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID查询
|
||||||
|
func (UserDAO) SelectOne(id uint) entity.User {
|
||||||
|
var user entity.User
|
||||||
|
config.SqlExcutor.First(&user, id)
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询所有记录
|
||||||
|
func (UserDAO) SelectAll() []entity.User {
|
||||||
|
var users []entity.User
|
||||||
|
config.SqlExcutor.Find(&users)
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
// 条件查询
|
||||||
|
func (UserDAO) SelectList(resource entity.User) []entity.User {
|
||||||
|
var users []entity.User
|
||||||
|
config.SqlExcutor.Where("username LIKE ?", "%"+resource.UserName+"%").Find(&users)
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
// 条件分页查询
|
||||||
|
func (UserDAO) SelectPage(dto dto.UserDTO) []entity.User {
|
||||||
|
var users []entity.User
|
||||||
|
|
||||||
|
page, pageSize := util.FixPage(dto.Page, dto.PageSize)
|
||||||
|
// 分页条件
|
||||||
|
pageConfig := config.SqlExcutor.Where("username LIKE ?", "%"+dto.Username+"%")
|
||||||
|
// 分页参数
|
||||||
|
pageConfig = pageConfig.Limit((page - 1) * pageSize).Offset(pageSize)
|
||||||
|
pageConfig.Find(&users)
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
func test() {
|
||||||
|
//
|
||||||
|
//user := entity.User{Username: "lingye"}
|
||||||
|
//
|
||||||
|
//userDAO := dao.UserDAO{}
|
||||||
|
//userDAO.Insert(&user)
|
||||||
|
//userDAO.SelectAll()
|
||||||
|
//userDAO.SelectOne(1)
|
||||||
|
//userDAO.SelectList(user)
|
||||||
|
//
|
||||||
|
//dto := dto2.UserDTO{}
|
||||||
|
//dto.Username = "测试"
|
||||||
|
//userDAO.SelectPage(dto)
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"lingye-gin/src/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
base.BaseDO
|
||||||
|
UserName string `gorm:"column:user_name;type:varchar(255);not null;index:idx_username" json:"username" form:"username"`
|
||||||
|
NickName string `gorm:"column:nick_name;type:varchar(255)" json:"nickname" form:"nickname"`
|
||||||
|
Email string `gorm:"column:email;type:varchar(255)" json:"email" form:"email"`
|
||||||
|
PhoneNumber string `gorm:"column:phone_number;type:varchar(255)" json:"phoneNumber" form:"phoneNumber"`
|
||||||
|
Sex string `gorm:"column:sex;type:varchar(255)" json:"sex" form:"sex"`
|
||||||
|
Avatar string `gorm:"column:avatar;type:varchar(255)" json:"avatar" form:"avatar"`
|
||||||
|
Password string `gorm:"column:password;type:varchar(255);not null" json:"password" form:"password"`
|
||||||
|
Status string `gorm:"column:status;type:varchar(255);not null" json:"status" form:"status"`
|
||||||
|
Remark string `gorm:"column:remark;type:text(0)" json:"remark" form:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置User的表名为`sys_user`
|
||||||
|
func (User) TableName() string {
|
||||||
|
return "sys_user"
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"lingye-gin/src/config"
|
||||||
|
"lingye-gin/src/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 初始化yaml配置
|
||||||
|
new(config.ApplicationProperties).Init()
|
||||||
|
// 初始化redis
|
||||||
|
new(middleware.RedisPool).Init()
|
||||||
|
// 初始化gorm
|
||||||
|
new(config.DataSourcePool).Connect().LoadEntity()
|
||||||
|
// 延时调用函数
|
||||||
|
defer config.SqlExcutor.Close()
|
||||||
|
// 加载jwt
|
||||||
|
new(middleware.JwtEngine).Init()
|
||||||
|
// 初始化gin
|
||||||
|
new(middleware.GinEngine).Start()
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"lingye-gin/src/config"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GinEngine struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (GinEngine) Start() {
|
||||||
|
config.Logger.Info("GinWebServer Init...")
|
||||||
|
if strings.Compare(config.AppProps.App.Mode, "debug") == 0 {
|
||||||
|
gin.SetMode(gin.DebugMode)
|
||||||
|
} else if strings.Compare(config.AppProps.App.Mode, "release") == 0 {
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
}
|
||||||
|
config.Logger.Info("GinWebServer Starting...")
|
||||||
|
engine := gin.Default()
|
||||||
|
engine.Use(config.LoggerToFile())
|
||||||
|
// 设置路由
|
||||||
|
new(GinRouter).Init(engine)
|
||||||
|
engine.Run(fmt.Sprintf("0.0.0.0:%d", config.AppProps.Server.Port))
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"lingye-gin/src/config"
|
||||||
|
"lingye-gin/src/rest"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GinRouter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v GinRouter) Init(r *gin.Engine) {
|
||||||
|
config.Logger.Info("GinRouter Init")
|
||||||
|
|
||||||
|
// 一般路由映射关系
|
||||||
|
groupMap := make(map[string]*gin.RouterGroup)
|
||||||
|
// api路由映射关系
|
||||||
|
apiUrls := make([]rest.RequestApi, 0)
|
||||||
|
// api组名称
|
||||||
|
apiGroupName := "api"
|
||||||
|
|
||||||
|
for _, normalRa := range rest.Urls {
|
||||||
|
// 判断是否在某一组下
|
||||||
|
if strings.Compare(normalRa.GroupName, "") == 0 {
|
||||||
|
// 批量处理
|
||||||
|
if handle(normalRa, r) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 分组名称
|
||||||
|
groupName := fmt.Sprintf("/%s", normalRa.GroupName)
|
||||||
|
|
||||||
|
// api组, 基于jwt验证
|
||||||
|
if strings.Compare(normalRa.GroupName, apiGroupName) == 0 {
|
||||||
|
apiUrls = append(apiUrls, normalRa)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分组不存在
|
||||||
|
if groupMap[normalRa.GroupName] == nil {
|
||||||
|
if normalRa.GroupHandleFunction == nil {
|
||||||
|
// 不存在分组过滤器
|
||||||
|
groupMap[normalRa.GroupName] = r.Group(groupName)
|
||||||
|
} else {
|
||||||
|
groupMap[normalRa.GroupName] = r.Group(groupName, normalRa.GroupHandleFunction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 批量处理
|
||||||
|
if handleGroup(normalRa, groupMap[normalRa.GroupName]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, apiRa := range apiUrls {
|
||||||
|
jwtR := r.Group(apiGroupName)
|
||||||
|
jwtR.Use(config.JwtHandle)
|
||||||
|
// 批量处理
|
||||||
|
if handleGroup(apiRa, jwtR) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Logger.Info("GinRouter Ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle(request rest.RequestApi, engine *gin.Engine) bool {
|
||||||
|
// get
|
||||||
|
if strings.Compare(request.Mode, "get") == 0 {
|
||||||
|
engine.GET(request.RelativePath, request.HandleFunction)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// post
|
||||||
|
if strings.Compare(request.Mode, "post") == 0 {
|
||||||
|
engine.POST(request.RelativePath, request.HandleFunction)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// delete
|
||||||
|
if strings.Compare(request.Mode, "delete") == 0 {
|
||||||
|
engine.DELETE(request.RelativePath, request.HandleFunction)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// put
|
||||||
|
if strings.Compare(request.Mode, "put") == 0 {
|
||||||
|
engine.PUT(request.RelativePath, request.HandleFunction)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGroup(request rest.RequestApi, group *gin.RouterGroup) bool {
|
||||||
|
// get
|
||||||
|
if strings.Compare(request.Mode, "get") == 0 {
|
||||||
|
group.GET(request.RelativePath, request.HandleFunction)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// post
|
||||||
|
if strings.Compare(request.Mode, "post") == 0 {
|
||||||
|
group.POST(request.RelativePath, request.HandleFunction)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// delete
|
||||||
|
if strings.Compare(request.Mode, "delete") == 0 {
|
||||||
|
group.DELETE(request.RelativePath, request.HandleFunction)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// put
|
||||||
|
if strings.Compare(request.Mode, "put") == 0 {
|
||||||
|
group.PUT(request.RelativePath, request.HandleFunction)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/dgrijalva/jwt-go"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"lingye-gin/src/config"
|
||||||
|
"lingye-gin/src/entity"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Jwt引擎
|
||||||
|
type JwtEngine struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 认证请求头
|
||||||
|
const HeadAuthorization = "Authorization"
|
||||||
|
const HeadLingYe = "LingYe"
|
||||||
|
|
||||||
|
// Payload 载荷
|
||||||
|
// 继承jwt提供给载荷,扩展自己所需字段
|
||||||
|
type JwtClaims struct {
|
||||||
|
jwt.StandardClaims
|
||||||
|
UserId uint `json:"id"` // 用户ID
|
||||||
|
UserName string `json:"username"` // 用户名
|
||||||
|
NickName string `json:"nickname"` // 用户昵称
|
||||||
|
Email string `json:"email"` // 邮箱账号
|
||||||
|
UserRole []string `json:"userRoles"` // 用户角色编码合集
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成令牌
|
||||||
|
func (JwtEngine) CreateJwtToken(user entity.User, timeout int) (string, bool) {
|
||||||
|
// Jwt Secret 私钥
|
||||||
|
jwtSecret := config.AppProps.Jwt.Secret
|
||||||
|
// 过期时间
|
||||||
|
expiredAt := time.Now().Add(time.Hour * time.Duration(timeout)).Unix()
|
||||||
|
// 设置载荷
|
||||||
|
claims := JwtClaims{}
|
||||||
|
claims.UserId = user.ID
|
||||||
|
claims.UserName = user.UserName
|
||||||
|
claims.NickName = user.NickName
|
||||||
|
claims.Email = user.Email
|
||||||
|
claims.ExpiresAt = expiredAt
|
||||||
|
// claims.Issuer = "ginv" // 非必须,也可以填充用户名
|
||||||
|
// 生成令牌 采用HMAC SHA256算法加密
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
|
||||||
|
// 令牌签名
|
||||||
|
tokenString, err := token.SignedString([]byte(jwtSecret))
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return tokenString, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证令牌
|
||||||
|
func (JwtEngine) validateJwt(tokenString string) (*JwtClaims, bool) {
|
||||||
|
// Jwt Secret 私钥
|
||||||
|
jwtSecret := config.AppProps.Jwt.Secret
|
||||||
|
// 解析令牌字符串
|
||||||
|
token, err := jwt.ParseWithClaims(tokenString, &JwtClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
return []byte(jwtSecret), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
config.Logger.Println(err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
// 获取载荷
|
||||||
|
claims, ok := token.Claims.(*JwtClaims)
|
||||||
|
if ok && token.Valid {
|
||||||
|
return claims, true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新Token
|
||||||
|
func (engine JwtEngine) RefreshToken(tokenString string) (string, bool) {
|
||||||
|
// Jwt Secret 私钥
|
||||||
|
jwtSecret := config.AppProps.Jwt.Secret
|
||||||
|
jwt.TimeFunc = func() time.Time {
|
||||||
|
return time.Unix(0, 0)
|
||||||
|
}
|
||||||
|
// 解析令牌字符串
|
||||||
|
token, err := jwt.ParseWithClaims(tokenString, &JwtClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
return []byte(jwtSecret), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
config.Logger.Println(err)
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
// 获取载荷
|
||||||
|
var claims *JwtClaims
|
||||||
|
var ok bool
|
||||||
|
if claims, ok = token.Claims.(*JwtClaims); ok && token.Valid {
|
||||||
|
jwt.TimeFunc = time.Now
|
||||||
|
// 过期时间
|
||||||
|
expiredAt := time.Now().Add(1 * time.Hour).Unix()
|
||||||
|
claims.StandardClaims.ExpiresAt = expiredAt
|
||||||
|
// 拷贝载体
|
||||||
|
currentUserInfo := entity.User{}
|
||||||
|
currentUserInfo.ID = claims.UserId
|
||||||
|
currentUserInfo.UserName = claims.UserName
|
||||||
|
currentUserInfo.NickName = claims.NickName
|
||||||
|
currentUserInfo.Email = claims.Email
|
||||||
|
return engine.CreateJwtToken(currentUserInfo, config.AppProps.Jwt.Expiry)
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
func (engine JwtEngine) Init() {
|
||||||
|
config.Logger.Info("JwtEngine Init...")
|
||||||
|
config.JwtHandle = func(c *gin.Context) {
|
||||||
|
// 获取请求头中的Authorization
|
||||||
|
authorization := c.Request.Header.Get(HeadAuthorization)
|
||||||
|
if strings.Compare(authorization, "") == 0 {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": http.StatusUnauthorized,
|
||||||
|
"msg": "Authorization Header Is None, No Permission",
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 拆分Authorization字段获取token字符串
|
||||||
|
strSlice := strings.SplitN(authorization, " ", 2)
|
||||||
|
if len(strSlice) != 2 || strings.Compare(HeadLingYe, strSlice[0]) != 0 {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": http.StatusUnauthorized,
|
||||||
|
"msg": "Authorization Header Format Is Error, No Permission",
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 验证token字符串
|
||||||
|
claim, ok := engine.validateJwt(strSlice[1])
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": http.StatusBadRequest,
|
||||||
|
"msg": "Token Validate Error",
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 过期判断
|
||||||
|
if time.Now().Unix() > claim.ExpiresAt {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": http.StatusBadRequest,
|
||||||
|
"msg": "Token Is Expired",
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 设置用户名
|
||||||
|
c.Set("username", claim.UserName)
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
config.Logger.Info("JwtEngine Ok...")
|
||||||
|
}
|
|
@ -0,0 +1,355 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-redis/redis"
|
||||||
|
"lingye-gin/src/config"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var RedisClient *redis.Client
|
||||||
|
|
||||||
|
type RedisPool struct{}
|
||||||
|
|
||||||
|
func (p RedisPool) Init() {
|
||||||
|
config.Logger.Info("RedisPool Init")
|
||||||
|
// 连接服务器
|
||||||
|
RedisClient = redis.NewClient(&redis.Options{
|
||||||
|
Addr: config.AppProps.Redis.Addr,
|
||||||
|
Password: config.AppProps.Redis.Passwd,
|
||||||
|
DB: config.AppProps.Redis.Database,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 心跳
|
||||||
|
pong, err := RedisClient.Ping().Result()
|
||||||
|
|
||||||
|
// Output: PONG <nil>
|
||||||
|
config.Logger.Info("RedisPool Ping And ", pong, err)
|
||||||
|
config.Logger.Info("RedisPool Ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p RedisPool) Test() {
|
||||||
|
ExampleClient_String()
|
||||||
|
ExampleClient_List()
|
||||||
|
ExampleClient_Hash()
|
||||||
|
ExampleClient_Set()
|
||||||
|
ExampleClient_SortSet()
|
||||||
|
ExampleClient_HyperLogLog()
|
||||||
|
ExampleClient_CMD()
|
||||||
|
ExampleClient_Scan()
|
||||||
|
ExampleClient_Tx()
|
||||||
|
ExampleClient_Script()
|
||||||
|
ExampleClient_PubSub()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClient_String() {
|
||||||
|
config.Logger.Println("ExampleClient_String")
|
||||||
|
defer config.Logger.Println("ExampleClient_String")
|
||||||
|
|
||||||
|
//kv读写
|
||||||
|
err := RedisClient.Set("key", "value", 1*time.Second).Err()
|
||||||
|
config.Logger.Println(err)
|
||||||
|
|
||||||
|
//获取过期时间
|
||||||
|
tm, err := RedisClient.TTL("key").Result()
|
||||||
|
config.Logger.Println(tm)
|
||||||
|
|
||||||
|
val, err := RedisClient.Get("key").Result()
|
||||||
|
config.Logger.Println(val, err)
|
||||||
|
|
||||||
|
val2, err := RedisClient.Get("missing_key").Result()
|
||||||
|
if err == redis.Nil {
|
||||||
|
config.Logger.Println("missing_key does not exist")
|
||||||
|
} else if err != nil {
|
||||||
|
config.Logger.Println("missing_key", val2, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//不存在才设置 过期时间 nx ex
|
||||||
|
value, err := RedisClient.SetNX("counter", 0, 1*time.Second).Result()
|
||||||
|
config.Logger.Println("setnx", value, err)
|
||||||
|
|
||||||
|
//Incr
|
||||||
|
result, err := RedisClient.Incr("counter").Result()
|
||||||
|
config.Logger.Println("Incr", result, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClient_List() {
|
||||||
|
config.Logger.Println("ExampleClient_List")
|
||||||
|
defer config.Logger.Println("ExampleClient_List")
|
||||||
|
|
||||||
|
//添加
|
||||||
|
config.Logger.Println(RedisClient.RPush("list_test", "message1").Err())
|
||||||
|
config.Logger.Println(RedisClient.RPush("list_test", "message2").Err())
|
||||||
|
|
||||||
|
//设置
|
||||||
|
config.Logger.Println(RedisClient.LSet("list_test", 2, "message set").Err())
|
||||||
|
|
||||||
|
//remove
|
||||||
|
ret, err := RedisClient.LRem("list_test", 3, "message1").Result()
|
||||||
|
config.Logger.Println(ret, err)
|
||||||
|
|
||||||
|
rLen, err := RedisClient.LLen("list_test").Result()
|
||||||
|
config.Logger.Println(rLen, err)
|
||||||
|
|
||||||
|
//遍历
|
||||||
|
lists, err := RedisClient.LRange("list_test", 0, rLen-1).Result()
|
||||||
|
config.Logger.Println("LRange", lists, err)
|
||||||
|
|
||||||
|
//pop没有时阻塞
|
||||||
|
result, err := RedisClient.BLPop(1*time.Second, "list_test").Result()
|
||||||
|
config.Logger.Println("result:", result, err, len(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClient_Hash() {
|
||||||
|
config.Logger.Println("ExampleClient_Hash")
|
||||||
|
defer config.Logger.Println("ExampleClient_Hash")
|
||||||
|
|
||||||
|
datas := map[string]interface{}{
|
||||||
|
"name": "LI LEI",
|
||||||
|
"sex": 1,
|
||||||
|
"age": 28,
|
||||||
|
"tel": 123445578,
|
||||||
|
}
|
||||||
|
|
||||||
|
//添加
|
||||||
|
if err := RedisClient.HMSet("hash_test", datas).Err(); err != nil {
|
||||||
|
config.Logger.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取
|
||||||
|
rets, err := RedisClient.HMGet("hash_test", "name", "sex").Result()
|
||||||
|
config.Logger.Println("rets:", rets, err)
|
||||||
|
|
||||||
|
//成员
|
||||||
|
retAll, err := RedisClient.HGetAll("hash_test").Result()
|
||||||
|
config.Logger.Println("retAll", retAll, err)
|
||||||
|
|
||||||
|
//存在
|
||||||
|
bExist, err := RedisClient.HExists("hash_test", "tel").Result()
|
||||||
|
config.Logger.Println(bExist, err)
|
||||||
|
|
||||||
|
bRet, err := RedisClient.HSetNX("hash_test", "id", 100).Result()
|
||||||
|
config.Logger.Println(bRet, err)
|
||||||
|
|
||||||
|
//删除
|
||||||
|
config.Logger.Println(RedisClient.HDel("hash_test", "age").Result())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClient_Set() {
|
||||||
|
config.Logger.Println("ExampleClient_Set")
|
||||||
|
defer config.Logger.Println("ExampleClient_Set")
|
||||||
|
|
||||||
|
//添加
|
||||||
|
ret, err := RedisClient.SAdd("set_test", "11", "22", "33", "44").Result()
|
||||||
|
config.Logger.Println(ret, err)
|
||||||
|
|
||||||
|
//数量
|
||||||
|
count, err := RedisClient.SCard("set_test").Result()
|
||||||
|
config.Logger.Println(count, err)
|
||||||
|
|
||||||
|
//删除
|
||||||
|
ret, err = RedisClient.SRem("set_test", "11", "22").Result()
|
||||||
|
config.Logger.Println(ret, err)
|
||||||
|
|
||||||
|
//成员
|
||||||
|
members, err := RedisClient.SMembers("set_test").Result()
|
||||||
|
config.Logger.Println(members, err)
|
||||||
|
|
||||||
|
bret, err := RedisClient.SIsMember("set_test", "33").Result()
|
||||||
|
config.Logger.Println(bret, err)
|
||||||
|
|
||||||
|
RedisClient.SAdd("set_a", "11", "22", "33", "44")
|
||||||
|
RedisClient.SAdd("set_b", "11", "22", "33", "55", "66", "77")
|
||||||
|
//差集
|
||||||
|
diff, err := RedisClient.SDiff("set_a", "set_b").Result()
|
||||||
|
config.Logger.Println(diff, err)
|
||||||
|
|
||||||
|
//交集
|
||||||
|
inter, err := RedisClient.SInter("set_a", "set_b").Result()
|
||||||
|
config.Logger.Println(inter, err)
|
||||||
|
|
||||||
|
//并集
|
||||||
|
union, err := RedisClient.SUnion("set_a", "set_b").Result()
|
||||||
|
config.Logger.Println(union, err)
|
||||||
|
|
||||||
|
ret, err = RedisClient.SDiffStore("set_diff", "set_a", "set_b").Result()
|
||||||
|
config.Logger.Println(ret, err)
|
||||||
|
|
||||||
|
rets, err := RedisClient.SMembers("set_diff").Result()
|
||||||
|
config.Logger.Println(rets, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClient_SortSet() {
|
||||||
|
config.Logger.Println("ExampleClient_SortSet")
|
||||||
|
defer config.Logger.Println("ExampleClient_SortSet")
|
||||||
|
|
||||||
|
addArgs := make([]redis.Z, 100)
|
||||||
|
for i := 1; i < 100; i++ {
|
||||||
|
addArgs = append(addArgs, redis.Z{Score: float64(i), Member: fmt.Sprintf("a_%d", i)})
|
||||||
|
}
|
||||||
|
//Logger.Println(addArgs)
|
||||||
|
|
||||||
|
Shuffle := func(slice []redis.Z) {
|
||||||
|
r := rand.New(rand.NewSource(time.Now().Unix()))
|
||||||
|
for len(slice) > 0 {
|
||||||
|
n := len(slice)
|
||||||
|
randIndex := r.Intn(n)
|
||||||
|
slice[n-1], slice[randIndex] = slice[randIndex], slice[n-1]
|
||||||
|
slice = slice[:n-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//随机打乱
|
||||||
|
Shuffle(addArgs)
|
||||||
|
|
||||||
|
//添加
|
||||||
|
ret, err := RedisClient.ZAddNX("sortset_test", addArgs...).Result()
|
||||||
|
config.Logger.Println(ret, err)
|
||||||
|
|
||||||
|
//获取指定成员score
|
||||||
|
score, err := RedisClient.ZScore("sortset_test", "a_10").Result()
|
||||||
|
config.Logger.Println(score, err)
|
||||||
|
|
||||||
|
//获取制定成员的索引
|
||||||
|
index, err := RedisClient.ZRank("sortset_test", "a_50").Result()
|
||||||
|
config.Logger.Println(index, err)
|
||||||
|
|
||||||
|
count, err := RedisClient.SCard("sortset_test").Result()
|
||||||
|
config.Logger.Println(count, err)
|
||||||
|
|
||||||
|
//返回有序集合指定区间内的成员
|
||||||
|
rets, err := RedisClient.ZRange("sortset_test", 10, 20).Result()
|
||||||
|
config.Logger.Println(rets, err)
|
||||||
|
|
||||||
|
//返回有序集合指定区间内的成员分数从高到低
|
||||||
|
rets, err = RedisClient.ZRevRange("sortset_test", 10, 20).Result()
|
||||||
|
config.Logger.Println(rets, err)
|
||||||
|
|
||||||
|
//指定分数区间的成员列表
|
||||||
|
rets, err = RedisClient.ZRangeByScore("sortset_test", redis.ZRangeBy{Min: "(30", Max: "(50", Offset: 1, Count: 10}).Result()
|
||||||
|
config.Logger.Println(rets, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。
|
||||||
|
//每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数
|
||||||
|
func ExampleClient_HyperLogLog() {
|
||||||
|
config.Logger.Println("ExampleClient_HyperLogLog")
|
||||||
|
defer config.Logger.Println("ExampleClient_HyperLogLog")
|
||||||
|
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
RedisClient.PFAdd("pf_test_1", fmt.Sprintf("pfkey%d", i))
|
||||||
|
}
|
||||||
|
ret, err := RedisClient.PFCount("pf_test_1").Result()
|
||||||
|
config.Logger.Println(ret, err)
|
||||||
|
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
RedisClient.PFAdd("pf_test_2", fmt.Sprintf("pfkey%d", i))
|
||||||
|
}
|
||||||
|
ret, err = RedisClient.PFCount("pf_test_2").Result()
|
||||||
|
config.Logger.Println(ret, err)
|
||||||
|
|
||||||
|
RedisClient.PFMerge("pf_test", "pf_test_2", "pf_test_1")
|
||||||
|
ret, err = RedisClient.PFCount("pf_test").Result()
|
||||||
|
config.Logger.Println(ret, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClient_PubSub() {
|
||||||
|
config.Logger.Println("ExampleClient_PubSub")
|
||||||
|
defer config.Logger.Println("ExampleClient_PubSub")
|
||||||
|
//发布订阅
|
||||||
|
pubsub := RedisClient.Subscribe("subkey")
|
||||||
|
_, err := pubsub.Receive()
|
||||||
|
if err != nil {
|
||||||
|
config.Logger.Fatal("pubsub.Receive")
|
||||||
|
}
|
||||||
|
ch := pubsub.Channel()
|
||||||
|
time.AfterFunc(1*time.Second, func() {
|
||||||
|
config.Logger.Println("Publish")
|
||||||
|
|
||||||
|
err = RedisClient.Publish("subkey", "test publish 1").Err()
|
||||||
|
if err != nil {
|
||||||
|
config.Logger.Fatal("RedisClient.Publish", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
RedisClient.Publish("subkey", "test publish 2")
|
||||||
|
})
|
||||||
|
for msg := range ch {
|
||||||
|
config.Logger.Println("recv channel:", msg.Channel, msg.Pattern, msg.Payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClient_CMD() {
|
||||||
|
config.Logger.Println("ExampleClient_CMD")
|
||||||
|
defer config.Logger.Println("ExampleClient_CMD")
|
||||||
|
|
||||||
|
//执行自定义redis命令
|
||||||
|
Get := func(rdb *redis.Client, key string) *redis.StringCmd {
|
||||||
|
cmd := redis.NewStringCmd("get", key)
|
||||||
|
RedisClient.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := Get(RedisClient, "NewStringCmd").Result()
|
||||||
|
config.Logger.Println("NewStringCmd", v, err)
|
||||||
|
|
||||||
|
v, err = RedisClient.Do("get", "RedisClient.do").String()
|
||||||
|
config.Logger.Println("RedisClient.Do", v, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClient_Scan() {
|
||||||
|
config.Logger.Println("ExampleClient_Scan")
|
||||||
|
defer config.Logger.Println("ExampleClient_Scan")
|
||||||
|
|
||||||
|
//scan
|
||||||
|
for i := 1; i < 1000; i++ {
|
||||||
|
RedisClient.Set(fmt.Sprintf("skey_%d", i), i, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
cusor := uint64(0)
|
||||||
|
for {
|
||||||
|
keys, retCusor, err := RedisClient.Scan(cusor, "skey_*", int64(100)).Result()
|
||||||
|
config.Logger.Println(keys, cusor, err)
|
||||||
|
cusor = retCusor
|
||||||
|
if cusor == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClient_Tx() {
|
||||||
|
pipe := RedisClient.TxPipeline()
|
||||||
|
incr := pipe.Incr("tx_pipeline_counter")
|
||||||
|
pipe.Expire("tx_pipeline_counter", time.Hour)
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
//
|
||||||
|
// MULTI
|
||||||
|
// INCR pipeline_counter
|
||||||
|
// EXPIRE pipeline_counts 3600
|
||||||
|
// EXEC
|
||||||
|
//
|
||||||
|
// using one rdb-server roundtrip.
|
||||||
|
_, err := pipe.Exec()
|
||||||
|
fmt.Println(incr.Val(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClient_Script() {
|
||||||
|
IncrByXX := redis.NewScript(`
|
||||||
|
if redis.call("GET", KEYS[1]) ~= false then
|
||||||
|
return redis.call("INCRBY", KEYS[1], ARGV[1])
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
`)
|
||||||
|
|
||||||
|
n, err := IncrByXX.Run(RedisClient, []string{"xx_counter"}, 2).Result()
|
||||||
|
fmt.Println(n, err)
|
||||||
|
|
||||||
|
err = RedisClient.Set("xx_counter", "40", 0).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = IncrByXX.Run(RedisClient, []string{"xx_counter"}, 2).Result()
|
||||||
|
fmt.Println(n, err)
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Student struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Age uint `json:"age"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func DescribeStudents(c *gin.Context) {
|
||||||
|
var students []Student
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"data": students,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 15,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"lingye-gin/src/util"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DescribeSign(c *gin.Context) {
|
||||||
|
ts := strconv.FormatInt(util.GetTimeUnix(), 10)
|
||||||
|
res := map[string]interface{}{}
|
||||||
|
params := url.Values{
|
||||||
|
"username": []string{"lingye"},
|
||||||
|
"age": []string{"10"},
|
||||||
|
"ts": []string{ts},
|
||||||
|
}
|
||||||
|
res["sn"] = util.CreateSign(params)
|
||||||
|
res["ts"] = ts
|
||||||
|
util.RetJson("200", "", res, c)
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
v3 "lingye-gin/src/rest/api"
|
||||||
|
v1 "lingye-gin/src/rest/v1"
|
||||||
|
v2 "lingye-gin/src/rest/v2"
|
||||||
|
"lingye-gin/src/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RequestApi struct {
|
||||||
|
// get、post、delete、put
|
||||||
|
Mode string
|
||||||
|
// 分组名称
|
||||||
|
GroupName string
|
||||||
|
// 分组过滤器
|
||||||
|
GroupHandleFunction gin.HandlerFunc
|
||||||
|
// 请求路径
|
||||||
|
RelativePath string
|
||||||
|
// 请求处理器
|
||||||
|
HandleFunction gin.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义变长数组变量
|
||||||
|
var Urls = [...]RequestApi{
|
||||||
|
// 定义请求方式和路径
|
||||||
|
{Mode: "get", RelativePath: "/sn", HandleFunction: DescribeSign},
|
||||||
|
// v1
|
||||||
|
// 获取所有学生
|
||||||
|
{GroupName: "v1", Mode: "get", RelativePath: "/students", HandleFunction: v1.DescribeStudents},
|
||||||
|
// 根据ID获取学生
|
||||||
|
{GroupName: "v1", Mode: "get", RelativePath: "/students/:id", HandleFunction: v1.DescribeStudentById},
|
||||||
|
// 保存学生
|
||||||
|
{GroupName: "v1", Mode: "post", RelativePath: "/students", HandleFunction: v1.CreateStudent},
|
||||||
|
// 根据ID更新学生
|
||||||
|
{GroupName: "v1", Mode: "put", RelativePath: "/students/:id", HandleFunction: v1.ModifyStudentById},
|
||||||
|
// 根据ID删除学生
|
||||||
|
{GroupName: "v1", Mode: "delete", RelativePath: "/students/:id", HandleFunction: v1.DeleteStudentById},
|
||||||
|
// v2
|
||||||
|
{GroupName: "v2", GroupHandleFunction: util.VerifySign, Mode: "get", RelativePath: "/students", HandleFunction: v2.DescribeStudents},
|
||||||
|
// api jwt
|
||||||
|
{GroupName: "api", Mode: "get", RelativePath: "/students", HandleFunction: v3.DescribeStudents},
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Student struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Age uint `json:"age"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func DescribeStudents(c *gin.Context) {
|
||||||
|
var students []Student
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"data": students,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 15,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func DescribeStudentById(c *gin.Context) {
|
||||||
|
id := c.Params.ByName("id")
|
||||||
|
fmt.Println("id=", id)
|
||||||
|
var student Student
|
||||||
|
if student.ID == 0 {
|
||||||
|
c.JSON(404, gin.H{"message": "student not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(200, student)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateStudent(c *gin.Context) {
|
||||||
|
var student Student
|
||||||
|
// 绑定一个请求主体到一个类型
|
||||||
|
_ = c.BindJSON(&student)
|
||||||
|
c.JSON(200, "创建成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModifyStudentById(c *gin.Context) {
|
||||||
|
id := c.Params.ByName("id")
|
||||||
|
fmt.Println("id=", id)
|
||||||
|
var student Student
|
||||||
|
if student.ID == 0 {
|
||||||
|
c.JSON(404, gin.H{"message": "student not found"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
_ = c.BindJSON(&student)
|
||||||
|
c.JSON(200, student)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteStudentById(c *gin.Context) {
|
||||||
|
id := c.Params.ByName("id")
|
||||||
|
fmt.Println("id=", id)
|
||||||
|
var student Student
|
||||||
|
if student.ID == 0 {
|
||||||
|
c.JSON(404, gin.H{"message": "student not found"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
_ = c.BindJSON(&student)
|
||||||
|
c.JSON(200, gin.H{"message": "delete success"})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Student struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Age uint `json:"age"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func DescribeStudents(c *gin.Context) {
|
||||||
|
var students []Student
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"data": students,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 15,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package dto
|
||||||
|
|
||||||
|
import "lingye-gin/src/base"
|
||||||
|
|
||||||
|
type UserDTO struct {
|
||||||
|
base.SimplePage
|
||||||
|
Username string
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"lingye-gin/src/config"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 打印
|
||||||
|
func Print(i interface{}) {
|
||||||
|
fmt.Println("---")
|
||||||
|
fmt.Println(i)
|
||||||
|
fmt.Println("---")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回JSON
|
||||||
|
func RetJson(code, msg string, data interface{}, c *gin.Context) {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": code,
|
||||||
|
"msg": msg,
|
||||||
|
"data": data,
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前时间戳
|
||||||
|
func GetTimeUnix() int64 {
|
||||||
|
return time.Now().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MD5 方法
|
||||||
|
func MD5(str string) string {
|
||||||
|
s := md5.New()
|
||||||
|
s.Write([]byte(str))
|
||||||
|
return hex.EncodeToString(s.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修正分页参数
|
||||||
|
func FixPage(page uint, pageSize uint) (p uint, ps uint) {
|
||||||
|
if page == 0 {
|
||||||
|
p = 1
|
||||||
|
}
|
||||||
|
if pageSize == 0 {
|
||||||
|
ps = 15
|
||||||
|
}
|
||||||
|
return p, ps
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成签名
|
||||||
|
func CreateSign(params url.Values) string {
|
||||||
|
var key []string
|
||||||
|
var str = ""
|
||||||
|
for k := range params {
|
||||||
|
if k != "sn" {
|
||||||
|
key = append(key, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(key)
|
||||||
|
for i := 0; i < len(key); i++ {
|
||||||
|
if i == 0 {
|
||||||
|
str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i]))
|
||||||
|
} else {
|
||||||
|
str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 自定义签名算法
|
||||||
|
sign := MD5(MD5(str) + MD5(config.AppProps.App.Name+config.AppProps.Jwt.Secret))
|
||||||
|
return sign
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证签名
|
||||||
|
func VerifySign(c *gin.Context) {
|
||||||
|
var method = c.Request.Method
|
||||||
|
var ts int64
|
||||||
|
var sn string
|
||||||
|
var req url.Values
|
||||||
|
|
||||||
|
if method == "GET" {
|
||||||
|
req = c.Request.URL.Query()
|
||||||
|
sn = c.Query("sn")
|
||||||
|
ts, _ = strconv.ParseInt(c.Query("ts"), 10, 64)
|
||||||
|
|
||||||
|
} else if method == "POST" {
|
||||||
|
_ = c.Request.ParseForm()
|
||||||
|
req = c.Request.PostForm
|
||||||
|
sn = c.PostForm("sn")
|
||||||
|
ts, _ = strconv.ParseInt(c.PostForm("ts"), 10, 64)
|
||||||
|
} else {
|
||||||
|
RetJson("500", "Illegal requests", "", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
exp, _ := strconv.ParseInt(string(rune(config.AppProps.Jwt.Expiry)), 10, 64)
|
||||||
|
|
||||||
|
// 验证过期时间
|
||||||
|
if ts > GetTimeUnix() || GetTimeUnix()-ts >= exp {
|
||||||
|
RetJson("500", "Ts Error", "", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证签名
|
||||||
|
if sn == "" || sn != CreateSign(req) {
|
||||||
|
RetJson("500", "Sn Error", "", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue