diff --git a/QMPlusServer/config/config.go b/QMPlusServer/config/config.go new file mode 100644 index 0000000000000000000000000000000000000000..0a21b08c7eff8fe905f2b839b307a08cda754004 --- /dev/null +++ b/QMPlusServer/config/config.go @@ -0,0 +1,38 @@ +package config + +import ( + "fmt" + "github.com/fsnotify/fsnotify" + "github.com/spf13/viper" +) + +type Config struct { + Admin Admin +} +type Admin struct { + UserName string + Password string + Path string + Dbname string + Config string +} + +var Dbconfig Config + +func init() { + v := viper.New() + v.SetConfigName("config") // 设置配置文件名 (不带后缀) + v.AddConfigPath("./config/dbconfig/") // 第一个搜索路径 + v.SetConfigType("json") + err := v.ReadInConfig() // 搜索路径,并读取配置数据 + if err != nil { + panic(fmt.Errorf("Fatal error config file: %s \n", err)) + } + v.WatchConfig() + v.OnConfigChange(func(e fsnotify.Event) { + fmt.Println("Config file changed:", e.Name) + }) + if err := v.Unmarshal(&Dbconfig); err != nil { + fmt.Println(err) + } +} diff --git a/QMPlusServer/config/dbconfig/config.json b/QMPlusServer/config/dbconfig/config.json new file mode 100644 index 0000000000000000000000000000000000000000..cf7d1e4cf84bddacbaddc169a3a204e69e83e6b9 --- /dev/null +++ b/QMPlusServer/config/dbconfig/config.json @@ -0,0 +1,9 @@ +{ + "admin": { + "userName": "root", + "password": "Aa@6447985", + "path": "127.0.0.1:3306", + "dbname": "zhongzerong", + "config": "charset=utf8&parseTime=True&loc=Local" + } +} \ No newline at end of file diff --git a/QMPlusServer/go.mod b/QMPlusServer/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..9acf3448668d722e9abb00ee1ca55f0a95ad23ba --- /dev/null +++ b/QMPlusServer/go.mod @@ -0,0 +1,21 @@ +module main + +go 1.12 + +require ( + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect + github.com/fsnotify/fsnotify v1.4.7 + github.com/gin-gonic/gin v1.4.0 + github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect + github.com/jinzhu/gorm v1.9.10 + github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect + github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f + github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect + github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 + github.com/satori/go.uuid v1.2.0 + github.com/sirupsen/logrus v1.2.0 + github.com/spf13/viper v1.4.0 + github.com/swaggo/gin-swagger v1.2.0 + github.com/tebeka/strftime v0.1.3 // indirect +) diff --git a/QMPlusServer/init/initMysql.go b/QMPlusServer/init/initMysql.go new file mode 100644 index 0000000000000000000000000000000000000000..7a36dc16c34750eb5e20ca1aa6ff164a7109cb25 --- /dev/null +++ b/QMPlusServer/init/initMysql.go @@ -0,0 +1,21 @@ +package init + +import ( + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/mysql" + "log" + "main/config" +) + +var DEFAULTDB *gorm.DB + +func InitMysql(admin config.Admin) { + if db, err := gorm.Open("mysql", admin.UserName+":"+admin.Password+"@("+admin.Path+")/"+admin.Dbname+"?"+admin.Config); err != nil { + log.Printf("DEFAULTDB数据库启动异常%S", err) + } else { + DEFAULTDB = db + DEFAULTDB.DB().SetMaxIdleConns(10) + DEFAULTDB.DB().SetMaxIdleConns(100) + DEFAULTDB.AutoMigrate() + } +} diff --git a/QMPlusServer/init/initRouter.go b/QMPlusServer/init/initRouter.go new file mode 100644 index 0000000000000000000000000000000000000000..835cacc046cb3bcd84938939f61b1c87f4ca443e --- /dev/null +++ b/QMPlusServer/init/initRouter.go @@ -0,0 +1,14 @@ +package init + +import ( + "github.com/gin-gonic/gin" + "github.com/swaggo/gin-swagger" + "github.com/swaggo/gin-swagger/swaggerFiles" +) + +var Router = gin.Default() + +func InitRouter() { + Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) + //Router.Use(middleware.Logger()) +} diff --git a/QMPlusServer/main.go b/QMPlusServer/main.go new file mode 100644 index 0000000000000000000000000000000000000000..bcb11334c103a9a0ebcc7c3056d4e5f3d9afa5f2 --- /dev/null +++ b/QMPlusServer/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "main/config" + "main/init" + "net/http" + "time" +) + +// @Summary 打印测试功能 +// @title Swagger Example API +// @version 0.0.1 +// @description This is a sample server Petstore server. +// @BasePath /api/v1 +// @Host 127.0.0.1:8080 +// @Produce json +// @Param name query string true "Name" +// @Success 200 {string} json "{"code":200,"data":"name","msg":"ok"}" +// @Router / [get] +func main() { + init.InitMysql(config.Dbconfig.Admin) + defer init.DEFAULTDB.Close() + init.InitRouter() + s := &http.Server{ + Addr: ":8888", + Handler: init.Router, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + } + _ = s.ListenAndServe() +} diff --git a/QMPlusServer/middleware/jwt.go b/QMPlusServer/middleware/jwt.go new file mode 100644 index 0000000000000000000000000000000000000000..9fb1ccf96c46fc0deb8c803d1d2be1bb2ec86b5f --- /dev/null +++ b/QMPlusServer/middleware/jwt.go @@ -0,0 +1,138 @@ +package middleware + +import ( + "errors" + "github.com/dgrijalva/jwt-go" + "github.com/gin-gonic/gin" + uuid "github.com/satori/go.uuid" + "net/http" + "time" +) + +func JWTAuth() gin.HandlerFunc { + return func(c *gin.Context) { + token := c.Request.Header.Get("x-token") + if token == "" { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "msg": "未登录或非法访问", + "data": gin.H{}, + }) + c.Abort() + return + } + j := NewJWT() + // parseToken 解析token包含的信息 + claims, err := j.ParseToken(token) + if err != nil { + if err == TokenExpired { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "msg": "授权已过期", + }) + c.Abort() + return + } + c.JSON(http.StatusOK, gin.H{ + "success": false, + "msg": err.Error(), + }) + c.Abort() + return + } + c.Set("claims", claims) + } +} + +type JWT struct { + SigningKey []byte +} + +var ( + TokenExpired error = errors.New("Token is expired") + TokenNotValidYet error = errors.New("Token not active yet") + TokenMalformed error = errors.New("That's not even a token") + TokenInvalid error = errors.New("Couldn't handle this token:") + SignKey string = "newtrekWang" +) + +type CustomClaims struct { + UUID uuid.UUID + ID uint + AuthorityID uint + jwt.StandardClaims +} + +func NewJWT() *JWT { + return &JWT{ + []byte(GetSignKey()), + } +} + +//获取token +func GetSignKey() string { + return SignKey +} + +// 这是SignKey +func SetSignKey(key string) string { + SignKey = key + return SignKey +} + +//创建一个token +func (j *JWT) CreateToken(claims CustomClaims) (string, error) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString(j.SigningKey) +} + +//解析 token +func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) { + token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) { + return j.SigningKey, nil + }) + if err != nil { + if ve, ok := err.(*jwt.ValidationError); ok { + if ve.Errors&jwt.ValidationErrorMalformed != 0 { + return nil, TokenMalformed + } else if ve.Errors&jwt.ValidationErrorExpired != 0 { + // Token is expired + return nil, TokenExpired + } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { + return nil, TokenNotValidYet + } else { + return nil, TokenInvalid + } + } + } + if token != nil { + if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { + return claims, nil + } + return nil, TokenInvalid + + } else { + return nil, TokenInvalid + + } + +} + +// 更新token +func (j *JWT) RefreshToken(tokenString string) (string, error) { + jwt.TimeFunc = func() time.Time { + return time.Unix(0, 0) + } + token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { + return j.SigningKey, nil + }) + if err != nil { + return "", err + } + if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { + jwt.TimeFunc = time.Now + claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix() + return j.CreateToken(*claims) + } + return "", TokenInvalid +} diff --git a/QMPlusServer/middleware/logger.go b/QMPlusServer/middleware/logger.go new file mode 100644 index 0000000000000000000000000000000000000000..a012ad848c14155986b39aa83ed445e44bcb05e5 --- /dev/null +++ b/QMPlusServer/middleware/logger.go @@ -0,0 +1,60 @@ +package middleware + +import ( + "fmt" + "github.com/gin-gonic/gin" + "github.com/lestrrat/go-file-rotatelogs" + "github.com/rifflock/lfshook" + "github.com/sirupsen/logrus" + "os" + "time" +) + +func Logger() gin.HandlerFunc { + logClient := logrus.New() + //禁止logrus的输出 + src, err := os.OpenFile(os.DevNull, os.O_APPEND|os.O_WRONLY, os.ModeAppend) + if err != nil { + fmt.Println("err", err) + } + logClient.Out = src + logClient.SetLevel(logrus.DebugLevel) + apiLogPath := "api.log" + logWriter, err := rotatelogs.New( + apiLogPath+".%Y-%m-%d-%H-%M.log", + rotatelogs.WithLinkName(apiLogPath), // 生成软链,指向最新日志文件 + rotatelogs.WithMaxAge(7*24*time.Hour), // 文件最大保存时间 + rotatelogs.WithRotationTime(24*time.Hour), // 日志切割时间间隔 + ) + writeMap := lfshook.WriterMap{ + logrus.InfoLevel: logWriter, + logrus.FatalLevel: logWriter, + } + lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{}) + logClient.AddHook(lfHook) + + return func(c *gin.Context) { + // 开始时间 + start := time.Now() + // 处理请求 + c.Next() + // 结束时间 + end := time.Now() + //执行时间 + latency := end.Sub(start) + + path := c.Request.URL.Path + clientIP := c.ClientIP() + method := c.Request.Method + statusCode := c.Writer.Status() + buf := make([]byte, 1024) + n, _ := c.Request.Body.Read(buf) + requestParams := buf[0:n] + logClient.Infof("| %3d | %13v | %15s | %s %s |%s|", + statusCode, + latency, + clientIP, + method, path, requestParams, + ) + } +} diff --git a/QMPlusServer/model/interface.go b/QMPlusServer/model/interface.go new file mode 100644 index 0000000000000000000000000000000000000000..45acda7d456a2ab3c1d07b5e3af271b3055d3974 --- /dev/null +++ b/QMPlusServer/model/interface.go @@ -0,0 +1,9 @@ +package model + +// 因为我也不确定项目要不要多人维护 所以定义了CURD接口 凡是对数据库进行简单CURD操作 请实现此接口 默认首位返回 error +type CURD interface { + Create() (error, interface{}) + Updata() (error, interface{}) + Read() (error, interface{}) + Delete() (error, interface{}) +} diff --git a/QMPlusServer/model/user.go b/QMPlusServer/model/user.go new file mode 100644 index 0000000000000000000000000000000000000000..b36e0c296dc23e83e02caa7e825fc8de4f133caf --- /dev/null +++ b/QMPlusServer/model/user.go @@ -0,0 +1,21 @@ +package model + +import ( + "github.com/jinzhu/gorm" + uuid "github.com/satori/go.uuid" +) + +type User struct { + gorm.Model `json:"-"` + UUID uuid.UUID `json:"uuid"` + UserName string `json:"userName"` + PassWord string `json:"passWord"` + NickName string `json:"nickName" gorm:"default:'galeone'"` + HeaderImg string `json:"headerImg" gorm:"default:'galeone'"` + //Propertie // 多余属性自行添加 + //PropertieId uint // 自动关联 Propertie 的Id 附加属性过多 建议创建一对一关系 +} + +//type Propertie struct { +// gorm.Model +//} diff --git a/QMPlusServer/tools/des.go b/QMPlusServer/tools/des.go new file mode 100644 index 0000000000000000000000000000000000000000..263a25b247159e3dc30bde26245ba3f5dedee7a8 --- /dev/null +++ b/QMPlusServer/tools/des.go @@ -0,0 +1,41 @@ +package tools + +import ( + "bytes" + "crypto/cipher" + "crypto/des" +) + +func padding(src []byte, blocksize int) []byte { + n := len(src) + padnum := blocksize - n%blocksize + pad := bytes.Repeat([]byte{byte(padnum)}, padnum) + dst := append(src, pad...) + return dst +} + +func unpadding(src []byte) []byte { + n := len(src) + unpadnum := int(src[n-1]) + dst := src[:n-unpadnum] + return dst +} + +func EncryptDES(src []byte) []byte { + key := []byte("qimiao66") + block, _ := des.NewCipher(key) + src = padding(src, block.BlockSize()) + blockmode := cipher.NewCBCEncrypter(block, key) + blockmode.CryptBlocks(src, src) + return src +} + +func DecryptDES(src []byte) []byte { + key := []byte("qimiao66") + block, _ := des.NewCipher(key) + blockmode := cipher.NewCBCDecrypter(block, key) + blockmode.CryptBlocks(src, src) + src = unpadding(src) + return src + +} diff --git a/QMPlusServer/tools/hasGap.go b/QMPlusServer/tools/hasGap.go new file mode 100644 index 0000000000000000000000000000000000000000..c765daf8bef8ad3b525a4087ed9f9543372c63ff --- /dev/null +++ b/QMPlusServer/tools/hasGap.go @@ -0,0 +1,34 @@ +// 空值校验工具 仅用于检验空字符串 其余类型请勿使用 + +package tools + +import ( + "errors" + "fmt" + "reflect" +) + +func HasGap(input interface{}) error { + getType := reflect.TypeOf(input) + fmt.Println("获取类型 :", getType.Name()) + + getValue := reflect.ValueOf(input) + fmt.Println("所有字段", getValue) + + // 获取方法字段 + for i := 0; i < getType.NumField(); i++ { + field := getType.Field(i) + value := getValue.Field(i).Interface() + fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value) + if value == "" { + return errors.New(fmt.Sprintf("%s为空", field.Name)) + } + } + // 获取方法 + // 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历 + //for i := 0; i < getType.NumMethod(); i++ { + // m := getType.Method(i) + // fmt.Printf("%s: %v\n", m.Name, m.Type) + //} + return nil +} diff --git a/QMPlusServer/tools/md5.go b/QMPlusServer/tools/md5.go new file mode 100644 index 0000000000000000000000000000000000000000..f1659cf49373eaee9aa3307e539fcac2693af043 --- /dev/null +++ b/QMPlusServer/tools/md5.go @@ -0,0 +1,12 @@ +package tools + +import ( + "crypto/md5" + "encoding/hex" +) + +func MD5V(str string) string { + h := md5.New() + h.Write([]byte(str)) + return hex.EncodeToString(h.Sum(nil)) +} diff --git a/README.md b/README.md index 9acc109a16713a90a4c8d06fca0c665c9ff11d48..bf2fa3244e650aeb31479f7407e61e64aa415c1c 100644 Binary files a/README.md and b/README.md differ