Init...
This commit is contained in:
parent
117dee486a
commit
2c65637812
|
@ -1,12 +1,21 @@
|
|||
.idea/
|
||||
.DS_Store
|
||||
bin/
|
||||
pkg/
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.log
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
83
README.md
83
README.md
|
@ -1,39 +1,64 @@
|
|||
# lingye-gin
|
||||
|
||||
#### 介绍
|
||||
{**以下是 Gitee 平台说明,您可以替换此简介**
|
||||
Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
|
||||
无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
|
||||
GO + Gin
|
||||
|
||||
#### 软件架构
|
||||
软件架构说明
|
||||
```text
|
||||
# go版本:1.16.3 --> from go version
|
||||
# goland版本:2020.2.4 --> from https://www.jetbrains.com/go/download/other.html
|
||||
|
||||
# bin 可执行文件执行路径
|
||||
# doc 文档、图片等资源
|
||||
# logs 日志目录
|
||||
# src 源码目录
|
||||
- config 配置目录
|
||||
- application_config.go 配置文件解析配置
|
||||
- common.go 全局变量
|
||||
- log_config.go 日志配置
|
||||
- middleware 中间件目录
|
||||
- gin_engine.go gin配置
|
||||
- gin_router.go 路由载入配置
|
||||
- pkg 第三方依赖包目录
|
||||
- rest
|
||||
- urls.go 路由配置(绑定路径和处理器之间的关系)
|
||||
- util
|
||||
- util.go 工具类
|
||||
- application.yml 主配置文件
|
||||
- application-dev.yml 环境配置文件
|
||||
- main.go 应用入口
|
||||
# go.mod 依赖说明文件
|
||||
```
|
||||
|
||||
#### 安装教程
|
||||
#### 配置Goland
|
||||
```text
|
||||
# File | Settings | Go
|
||||
1、GOROOT
|
||||
Add SDK...
|
||||
Local...(这里选择你的Go安装的根路径,我的是"/usr/local/go")
|
||||
2、Go Modules
|
||||
- Enabel Go modules integration打勾
|
||||
-
|
||||
3、Go设置代理
|
||||
- 查看go 的环境变量 在cmd中 输入go env设置GOPROXY代理:
|
||||
go env -w GO111MODULE=on
|
||||
go env -w GOPROXY=https://goproxy.cn,direct
|
||||
- 设置GOPRIVATE来跳过私有库,比如常用的Gitee,中间使用逗号分隔:
|
||||
go env -w GOPRIVATE=*.gitee.com
|
||||
- 如果在运行go mod vendor时,提示Get https://sum.golang.org/lookup/xxxxxx: dial tcp 216.58.200.49:443: i/o timeout,则是因为Go 1.13设置了默认的GOSUMDB=sum.golang.org,这个网站是被墙了的,用于验证包的有效性,可以通过如下命令关闭:
|
||||
go env -w GOSUMDB=off
|
||||
- 可以设置 GOSUMDB="sum.golang.google.cn", 这个是专门为国内提供的sum 验证服务。
|
||||
go env -w GOSUMDB="sum.golang.google.cn"
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
# Apply,OK
|
||||
```
|
||||
|
||||
#### 使用说明
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### 参与贡献
|
||||
|
||||
1. Fork 本仓库
|
||||
2. 新建 Feat_xxx 分支
|
||||
3. 提交代码
|
||||
4. 新建 Pull Request
|
||||
|
||||
|
||||
#### 特技
|
||||
|
||||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
|
||||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
|
||||
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
|
||||
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
|
||||
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
- 配置GOPATH
|
||||
- Preferences | Go | GOPATH | Project GOPATH
|
||||
- add /usr/local/gitee/lingye-go-base/src (这个路径以你本地的路径为准)
|
||||
- Index entire GOPATH, 打勾
|
||||
|
||||
- 转到src下,执行命令: go mod tidy, 安装依赖包
|
||||
- 运行配置
|
||||
![avatar](./doc/images/build_config.png)
|
|
@ -0,0 +1,21 @@
|
|||
app:
|
||||
mode: debug
|
||||
name: LingYeGin
|
||||
# MD5 SHA256
|
||||
secret: 878bb8ef8807b5ddbcbddbf909e5526262cf74a54fb523cee17e5b80a1b7510d
|
||||
|
||||
server:
|
||||
port: 8088
|
||||
api:
|
||||
expiry: 120
|
||||
|
||||
log:
|
||||
file:
|
||||
path: ../logs
|
||||
name: lingyeGin.log
|
||||
level: debug
|
||||
|
||||
redis:
|
||||
addr: localhost:6379
|
||||
passwd:
|
||||
database: 10
|
|
@ -0,0 +1,2 @@
|
|||
app:
|
||||
env: dev
|
|
@ -0,0 +1,96 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v2"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 配置文件
|
||||
type AppConfig struct {
|
||||
// 环境
|
||||
Env string `yaml:"env"`
|
||||
// 运行模式
|
||||
Mode string `yaml:"mode"`
|
||||
// 应用名称
|
||||
Name string `yaml:"name"`
|
||||
// 加密盐
|
||||
Secret string `yaml:"secret"`
|
||||
}
|
||||
type ServerConfig struct {
|
||||
// 服务端口
|
||||
Port int `yaml:"port"`
|
||||
// API相关配置
|
||||
Api ServerApiConfig `yaml:"api"`
|
||||
}
|
||||
type ServerApiConfig struct {
|
||||
// 过期时间
|
||||
Expiry string `yaml:"expiry"`
|
||||
}
|
||||
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 ApplicationProperties struct {
|
||||
// 应用配置
|
||||
App AppConfig `yaml:"app"`
|
||||
// Gin服务配置
|
||||
Server ServerConfig `yaml:"server"`
|
||||
// 日志配置
|
||||
Log LogConfig `yaml:"log"`
|
||||
// redis配置
|
||||
Redis RedisConfig `yaml:"redis"`
|
||||
}
|
||||
|
||||
// 根据路径读取yaml文件
|
||||
func readYaml(path string) ApplicationProperties {
|
||||
var result ApplicationProperties
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatalf("File reading error, application.yml not exist!")
|
||||
}
|
||||
err = yaml.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot unmarshal data: %v", 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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return strings.Replace(dir, "\\", "/", -1)
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package config
|
||||
|
||||
// 应用配置
|
||||
var AppProps ApplicationProperties
|
|
@ -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,356 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-redis/redis"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
var redisdb *redis.Client
|
||||
var RedisClient *redis.Client
|
||||
|
||||
type RedisPool struct{}
|
||||
|
||||
func (p RedisPool) Init() {
|
||||
// 连接服务器
|
||||
redisdb = redis.NewClient(&redis.Options{
|
||||
Addr: AppProps.Redis.Addr,
|
||||
Password: AppProps.Redis.Passwd,
|
||||
DB: AppProps.Redis.Database,
|
||||
})
|
||||
|
||||
// 开放
|
||||
RedisClient = redisdb
|
||||
|
||||
// 心跳
|
||||
pong, err := redisdb.Ping().Result()
|
||||
|
||||
// Output: PONG <nil>
|
||||
Logger.Info(pong, err)
|
||||
}
|
||||
|
||||
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() {
|
||||
Logger.Println("ExampleClient_String")
|
||||
defer Logger.Println("ExampleClient_String")
|
||||
|
||||
//kv读写
|
||||
err := redisdb.Set("key", "value", 1*time.Second).Err()
|
||||
Logger.Println(err)
|
||||
|
||||
//获取过期时间
|
||||
tm, err := redisdb.TTL("key").Result()
|
||||
Logger.Println(tm)
|
||||
|
||||
val, err := redisdb.Get("key").Result()
|
||||
Logger.Println(val, err)
|
||||
|
||||
val2, err := redisdb.Get("missing_key").Result()
|
||||
if err == redis.Nil {
|
||||
Logger.Println("missing_key does not exist")
|
||||
} else if err != nil {
|
||||
Logger.Println("missing_key", val2, err)
|
||||
}
|
||||
|
||||
//不存在才设置 过期时间 nx ex
|
||||
value, err := redisdb.SetNX("counter", 0, 1*time.Second).Result()
|
||||
Logger.Println("setnx", value, err)
|
||||
|
||||
//Incr
|
||||
result, err := redisdb.Incr("counter").Result()
|
||||
Logger.Println("Incr", result, err)
|
||||
}
|
||||
|
||||
func ExampleClient_List() {
|
||||
Logger.Println("ExampleClient_List")
|
||||
defer Logger.Println("ExampleClient_List")
|
||||
|
||||
//添加
|
||||
Logger.Println(redisdb.RPush("list_test", "message1").Err())
|
||||
Logger.Println(redisdb.RPush("list_test", "message2").Err())
|
||||
|
||||
//设置
|
||||
Logger.Println(redisdb.LSet("list_test", 2, "message set").Err())
|
||||
|
||||
//remove
|
||||
ret, err := redisdb.LRem("list_test", 3, "message1").Result()
|
||||
Logger.Println(ret, err)
|
||||
|
||||
rLen, err := redisdb.LLen("list_test").Result()
|
||||
Logger.Println(rLen, err)
|
||||
|
||||
//遍历
|
||||
lists, err := redisdb.LRange("list_test", 0, rLen-1).Result()
|
||||
Logger.Println("LRange", lists, err)
|
||||
|
||||
//pop没有时阻塞
|
||||
result, err := redisdb.BLPop(1*time.Second, "list_test").Result()
|
||||
Logger.Println("result:", result, err, len(result))
|
||||
}
|
||||
|
||||
func ExampleClient_Hash() {
|
||||
Logger.Println("ExampleClient_Hash")
|
||||
defer Logger.Println("ExampleClient_Hash")
|
||||
|
||||
datas := map[string]interface{}{
|
||||
"name": "LI LEI",
|
||||
"sex": 1,
|
||||
"age": 28,
|
||||
"tel": 123445578,
|
||||
}
|
||||
|
||||
//添加
|
||||
if err := redisdb.HMSet("hash_test", datas).Err(); err != nil {
|
||||
Logger.Fatal(err)
|
||||
}
|
||||
|
||||
//获取
|
||||
rets, err := redisdb.HMGet("hash_test", "name", "sex").Result()
|
||||
Logger.Println("rets:", rets, err)
|
||||
|
||||
//成员
|
||||
retAll, err := redisdb.HGetAll("hash_test").Result()
|
||||
Logger.Println("retAll", retAll, err)
|
||||
|
||||
//存在
|
||||
bExist, err := redisdb.HExists("hash_test", "tel").Result()
|
||||
Logger.Println(bExist, err)
|
||||
|
||||
bRet, err := redisdb.HSetNX("hash_test", "id", 100).Result()
|
||||
Logger.Println(bRet, err)
|
||||
|
||||
//删除
|
||||
Logger.Println(redisdb.HDel("hash_test", "age").Result())
|
||||
}
|
||||
|
||||
func ExampleClient_Set() {
|
||||
Logger.Println("ExampleClient_Set")
|
||||
defer Logger.Println("ExampleClient_Set")
|
||||
|
||||
//添加
|
||||
ret, err := redisdb.SAdd("set_test", "11", "22", "33", "44").Result()
|
||||
Logger.Println(ret, err)
|
||||
|
||||
//数量
|
||||
count, err := redisdb.SCard("set_test").Result()
|
||||
Logger.Println(count, err)
|
||||
|
||||
//删除
|
||||
ret, err = redisdb.SRem("set_test", "11", "22").Result()
|
||||
Logger.Println(ret, err)
|
||||
|
||||
//成员
|
||||
members, err := redisdb.SMembers("set_test").Result()
|
||||
Logger.Println(members, err)
|
||||
|
||||
bret, err := redisdb.SIsMember("set_test", "33").Result()
|
||||
Logger.Println(bret, err)
|
||||
|
||||
redisdb.SAdd("set_a", "11", "22", "33", "44")
|
||||
redisdb.SAdd("set_b", "11", "22", "33", "55", "66", "77")
|
||||
//差集
|
||||
diff, err := redisdb.SDiff("set_a", "set_b").Result()
|
||||
Logger.Println(diff, err)
|
||||
|
||||
//交集
|
||||
inter, err := redisdb.SInter("set_a", "set_b").Result()
|
||||
Logger.Println(inter, err)
|
||||
|
||||
//并集
|
||||
union, err := redisdb.SUnion("set_a", "set_b").Result()
|
||||
Logger.Println(union, err)
|
||||
|
||||
ret, err = redisdb.SDiffStore("set_diff", "set_a", "set_b").Result()
|
||||
Logger.Println(ret, err)
|
||||
|
||||
rets, err := redisdb.SMembers("set_diff").Result()
|
||||
Logger.Println(rets, err)
|
||||
}
|
||||
|
||||
func ExampleClient_SortSet() {
|
||||
Logger.Println("ExampleClient_SortSet")
|
||||
defer 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 := redisdb.ZAddNX("sortset_test", addArgs...).Result()
|
||||
Logger.Println(ret, err)
|
||||
|
||||
//获取指定成员score
|
||||
score, err := redisdb.ZScore("sortset_test", "a_10").Result()
|
||||
Logger.Println(score, err)
|
||||
|
||||
//获取制定成员的索引
|
||||
index, err := redisdb.ZRank("sortset_test", "a_50").Result()
|
||||
Logger.Println(index, err)
|
||||
|
||||
count, err := redisdb.SCard("sortset_test").Result()
|
||||
Logger.Println(count, err)
|
||||
|
||||
//返回有序集合指定区间内的成员
|
||||
rets, err := redisdb.ZRange("sortset_test", 10, 20).Result()
|
||||
Logger.Println(rets, err)
|
||||
|
||||
//返回有序集合指定区间内的成员分数从高到低
|
||||
rets, err = redisdb.ZRevRange("sortset_test", 10, 20).Result()
|
||||
Logger.Println(rets, err)
|
||||
|
||||
//指定分数区间的成员列表
|
||||
rets, err = redisdb.ZRangeByScore("sortset_test", redis.ZRangeBy{Min: "(30", Max: "(50", Offset: 1, Count: 10}).Result()
|
||||
Logger.Println(rets, err)
|
||||
}
|
||||
|
||||
//用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。
|
||||
//每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数
|
||||
func ExampleClient_HyperLogLog() {
|
||||
Logger.Println("ExampleClient_HyperLogLog")
|
||||
defer Logger.Println("ExampleClient_HyperLogLog")
|
||||
|
||||
for i := 0; i < 10000; i++ {
|
||||
redisdb.PFAdd("pf_test_1", fmt.Sprintf("pfkey%d", i))
|
||||
}
|
||||
ret, err := redisdb.PFCount("pf_test_1").Result()
|
||||
Logger.Println(ret, err)
|
||||
|
||||
for i := 0; i < 10000; i++ {
|
||||
redisdb.PFAdd("pf_test_2", fmt.Sprintf("pfkey%d", i))
|
||||
}
|
||||
ret, err = redisdb.PFCount("pf_test_2").Result()
|
||||
Logger.Println(ret, err)
|
||||
|
||||
redisdb.PFMerge("pf_test", "pf_test_2", "pf_test_1")
|
||||
ret, err = redisdb.PFCount("pf_test").Result()
|
||||
Logger.Println(ret, err)
|
||||
}
|
||||
|
||||
func ExampleClient_PubSub() {
|
||||
Logger.Println("ExampleClient_PubSub")
|
||||
defer Logger.Println("ExampleClient_PubSub")
|
||||
//发布订阅
|
||||
pubsub := redisdb.Subscribe("subkey")
|
||||
_, err := pubsub.Receive()
|
||||
if err != nil {
|
||||
Logger.Fatal("pubsub.Receive")
|
||||
}
|
||||
ch := pubsub.Channel()
|
||||
time.AfterFunc(1*time.Second, func() {
|
||||
Logger.Println("Publish")
|
||||
|
||||
err = redisdb.Publish("subkey", "test publish 1").Err()
|
||||
if err != nil {
|
||||
Logger.Fatal("redisdb.Publish", err)
|
||||
}
|
||||
|
||||
redisdb.Publish("subkey", "test publish 2")
|
||||
})
|
||||
for msg := range ch {
|
||||
Logger.Println("recv channel:", msg.Channel, msg.Pattern, msg.Payload)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleClient_CMD() {
|
||||
Logger.Println("ExampleClient_CMD")
|
||||
defer Logger.Println("ExampleClient_CMD")
|
||||
|
||||
//执行自定义redis命令
|
||||
Get := func(rdb *redis.Client, key string) *redis.StringCmd {
|
||||
cmd := redis.NewStringCmd("get", key)
|
||||
redisdb.Process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
v, err := Get(redisdb, "NewStringCmd").Result()
|
||||
Logger.Println("NewStringCmd", v, err)
|
||||
|
||||
v, err = redisdb.Do("get", "redisdb.do").String()
|
||||
Logger.Println("redisdb.Do", v, err)
|
||||
}
|
||||
|
||||
func ExampleClient_Scan() {
|
||||
Logger.Println("ExampleClient_Scan")
|
||||
defer Logger.Println("ExampleClient_Scan")
|
||||
|
||||
//scan
|
||||
for i := 1; i < 1000; i++ {
|
||||
redisdb.Set(fmt.Sprintf("skey_%d", i), i, 0)
|
||||
}
|
||||
|
||||
cusor := uint64(0)
|
||||
for {
|
||||
keys, retCusor, err := redisdb.Scan(cusor, "skey_*", int64(100)).Result()
|
||||
Logger.Println(keys, cusor, err)
|
||||
cusor = retCusor
|
||||
if cusor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleClient_Tx() {
|
||||
pipe := redisdb.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(redisdb, []string{"xx_counter"}, 2).Result()
|
||||
fmt.Println(n, err)
|
||||
|
||||
err = redisdb.Set("xx_counter", "40", 0).Err()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
n, err = IncrByXX.Run(redisdb, []string{"xx_counter"}, 2).Result()
|
||||
fmt.Println(n, err)
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"lingye-gin/src/config"
|
||||
"lingye-gin/src/middleware"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 初始化yaml配置
|
||||
new(config.ApplicationProperties).Init()
|
||||
// 初始化redis
|
||||
new(config.RedisPool).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 (v *GinEngine) Start() {
|
||||
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)
|
||||
}
|
||||
engine := gin.Default()
|
||||
engine.Use(config.LoggerToFile())
|
||||
// 设置路由
|
||||
new(GinRouter).Init(engine)
|
||||
config.Logger.Info("GinWebServer Init...")
|
||||
_ = engine.Run(fmt.Sprintf("0.0.0.0:%d", config.AppProps.Server.Port))
|
||||
config.Logger.Info("GinWebServer Starting...")
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"lingye-gin/src/rest"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type GinRouter struct {
|
||||
}
|
||||
|
||||
func (v GinRouter) Init(r *gin.Engine) {
|
||||
// 路由组映射关系
|
||||
groupMap := make(map[string]*gin.RouterGroup)
|
||||
|
||||
for _, currentRa := range rest.Urls {
|
||||
// 判断是否在某一组下
|
||||
if strings.Compare(currentRa.GroupName, "") == 0 {
|
||||
// get
|
||||
if strings.Compare(currentRa.Mode, "get") == 0 {
|
||||
r.GET(currentRa.RelativePath, currentRa.HandleFunction)
|
||||
continue
|
||||
}
|
||||
// post
|
||||
if strings.Compare(currentRa.Mode, "post") == 0 {
|
||||
r.POST(currentRa.RelativePath, currentRa.HandleFunction)
|
||||
continue
|
||||
}
|
||||
// delete
|
||||
if strings.Compare(currentRa.Mode, "delete") == 0 {
|
||||
r.DELETE(currentRa.RelativePath, currentRa.HandleFunction)
|
||||
continue
|
||||
}
|
||||
// put
|
||||
if strings.Compare(currentRa.Mode, "put") == 0 {
|
||||
r.PUT(currentRa.RelativePath, currentRa.HandleFunction)
|
||||
}
|
||||
} else {
|
||||
// 分组名称
|
||||
groupName := fmt.Sprintf("/%s", currentRa.GroupName)
|
||||
// 分组不存在
|
||||
if groupMap[currentRa.GroupName] == nil {
|
||||
if currentRa.GroupHandleFunction == nil {
|
||||
// 不存在分组过滤器
|
||||
groupMap[currentRa.GroupName] = r.Group(groupName)
|
||||
} else {
|
||||
groupMap[currentRa.GroupName] = r.Group(groupName, currentRa.GroupHandleFunction)
|
||||
}
|
||||
}
|
||||
// get
|
||||
if strings.Compare(currentRa.Mode, "get") == 0 {
|
||||
groupMap[currentRa.GroupName].GET(currentRa.RelativePath, currentRa.HandleFunction)
|
||||
continue
|
||||
}
|
||||
// post
|
||||
if strings.Compare(currentRa.Mode, "post") == 0 {
|
||||
groupMap[currentRa.GroupName].POST(currentRa.RelativePath, currentRa.HandleFunction)
|
||||
continue
|
||||
}
|
||||
// delete
|
||||
if strings.Compare(currentRa.Mode, "delete") == 0 {
|
||||
groupMap[currentRa.GroupName].DELETE(currentRa.RelativePath, currentRa.HandleFunction)
|
||||
continue
|
||||
}
|
||||
// put
|
||||
if strings.Compare(currentRa.Mode, "put") == 0 {
|
||||
groupMap[currentRa.GroupName].PUT(currentRa.RelativePath, currentRa.HandleFunction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,40 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
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},
|
||||
}
|
|
@ -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,102 @@
|
|||
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 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.App.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(config.AppProps.Server.Api.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