Browse Source

项目基本构成

pixelqm 5 years ago
parent
commit
84a9cbda29

+ 38 - 0
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)
+	}
+}

+ 9 - 0
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"
+  }
+}

+ 21 - 0
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
+)

+ 21 - 0
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()
+	}
+}

+ 14 - 0
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())
+}

+ 32 - 0
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()
+}

+ 138 - 0
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
+}

+ 60 - 0
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,
+		)
+	}
+}

+ 9 - 0
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{})
+}

+ 21 - 0
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
+//}

+ 41 - 0
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
+
+}

+ 34 - 0
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
+}

+ 12 - 0
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))
+}

BIN
README.md