From ca47c25386895a812fd136a6d773366f2ac0f4b2 Mon Sep 17 00:00:00 2001 From: hehui Date: Sat, 3 Aug 2019 00:22:47 +0800 Subject: [PATCH] =?UTF-8?q?[ADD]=E9=A1=B9=E7=9B=AE=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=EF=BC=8C=E5=8C=85=E6=8B=ACgorm=E3=80=81gin=E3=80=81jw?= =?UTF-8?q?t=E4=BB=A5=E5=8F=8Aswagger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 18 +++++ README.md | 10 +++ api/auth.go | 55 ++++++++++++++ api/v1/game.go | 138 +++++++++++++++++++++++++++++++++++ docs/docs.go | 106 +++++++++++++++++++++++++++ docs/swagger.json | 42 +++++++++++ docs/swagger.yaml | 28 +++++++ main.go | 56 ++++++++++++++ middleware/jwt/jwt.go | 42 +++++++++++ models/auth.go | 16 ++++ models/game.go | 60 +++++++++++++++ models/models.go | 70 ++++++++++++++++++ pkg/e/code.go | 19 +++++ pkg/e/msg.go | 27 +++++++ pkg/logging/file.go | 51 +++++++++++++ pkg/logging/log.go | 73 ++++++++++++++++++ pkg/setting/setting.go | 59 +++++++++++++++ pkg/util/jwt.go | 48 ++++++++++++ pkg/util/pagination.go | 18 +++++ routers/router.go | 25 +++++++ routers/v1/game.go | 23 ++++++ runtime/logs/log20190802.log | 40 ++++++++++ runtime/logs/log20190803.log | 1 + 23 files changed, 1025 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 api/auth.go create mode 100644 api/v1/game.go create mode 100644 docs/docs.go create mode 100644 docs/swagger.json create mode 100644 docs/swagger.yaml create mode 100644 main.go create mode 100644 middleware/jwt/jwt.go create mode 100644 models/auth.go create mode 100644 models/game.go create mode 100644 models/models.go create mode 100644 pkg/e/code.go create mode 100644 pkg/e/msg.go create mode 100644 pkg/logging/file.go create mode 100644 pkg/logging/log.go create mode 100644 pkg/setting/setting.go create mode 100644 pkg/util/jwt.go create mode 100644 pkg/util/pagination.go create mode 100644 routers/router.go create mode 100644 routers/v1/game.go create mode 100644 runtime/logs/log20190802.log create mode 100644 runtime/logs/log20190803.log diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20a69a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# 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/ +conf/app.ini +.idea/* +main \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..55d861a --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# game_demo +```bush +game_demo/ +├── conf # 用于存储配置文件 +├── middleware # 应用中间件 +├── models # 应用数据库模型 +├── pkg # 第三方包 +├── routers # 路由逻辑处理 +└── runtime # 应用运行时数据 +``` \ No newline at end of file diff --git a/api/auth.go b/api/auth.go new file mode 100644 index 0000000..423ee8c --- /dev/null +++ b/api/auth.go @@ -0,0 +1,55 @@ +package api + +import ( + "game_demo/models" + "game_demo/pkg/e" + "game_demo/pkg/logging" + "game_demo/pkg/util" + "github.com/astaxie/beego/validation" + "github.com/gin-gonic/gin" + "net/http" +) + +type auth struct { + Username string `valid:"Required; MaxSize(50)"` + Password string `valid:"Required; MaxSize(50)"` +} + +func GetAuth(c *gin.Context) { + username := c.Query("username") + password := c.Query("password") + + valid := validation.Validation{} + a := auth{Username : username, Password : password} + ok, err := valid.Valid(&a) + + logging.Info(err) + + data := make(map[string]interface{}) + code := e.INVALID_PARAMS + + if ok { + isExist := models.CheckAuth(username, password) + if isExist { + token, err := util.GenerateToken(username, password) + if err != nil { + code = e.ERROR_AUTH_TOKEN + } else { + data["token"] = token + code = e.SUCCESS + } + } else { + code = e.ERROR_AUTH + } + } else { + for _, err := range valid.Errors { + logging.Info(err.Key, err.Message) + } + } + + c.JSON(http.StatusOK, gin.H{ + "code": code, + "msg" : e.GetMsg(code), + "data" : data, + }) +} \ No newline at end of file diff --git a/api/v1/game.go b/api/v1/game.go new file mode 100644 index 0000000..972ec89 --- /dev/null +++ b/api/v1/game.go @@ -0,0 +1,138 @@ +package v1 + +import ( + "game_demo/models" + "game_demo/pkg/e" + "game_demo/pkg/logging" + "game_demo/pkg/setting" + "game_demo/pkg/util" + "github.com/Unknwon/com" + "github.com/astaxie/beego/validation" + "github.com/gin-gonic/gin" + "io/ioutil" + "net/http" +) + +// @Summary 获取多个游戏 +// @Description +// @Accept json +// @Produce json +// @Param Authorization header string true "auth by /auth" +// @Success 200 {string} json "{"code" : 200, "data" : {}, msg: "ok"}" +// @Router /v1/games [get] +func GetGames(c *gin.Context) { + name := c.Query("name") + + maps := make(map[string]interface{}) + data := make(map[string]interface{}) + + if name != "" { + maps["name"] = name + } + + code := e.SUCCESS + + data["lists"] = models.GetGames(util.GetPage(c), setting.PageSize, maps) + data["total"] = models.GetGameTotal(maps) + + c.JSON(http.StatusOK, gin.H{ + "code": code, + "msg": e.GetMsg(code), + "data": data, + }) +} + +// 新增游戏 +func AddGame(c *gin.Context) { + name := c.PostForm("name") + cnName := c.PostForm("cn_name") + logo := c.PostForm("logo") + + valid := validation.Validation{} + valid.Required(name, "name").Message("名称不能为空") + valid.Required(cnName, "cn_name").Message("英文名称不能为空") + valid.Required(logo, "logo").Message("logo不能为空") + + code := e.INVALID_PARAMS + + logging.Info(ioutil.ReadAll(c.Request.Body)) + logging.Info(valid) + if ! valid.HasErrors() { + if ! models.ExistGameByName(name) { + code = e.SUCCESS + models.AddGame(name, cnName, logo) + } else { + code = e.ERROR_EXIST_GAME + } + } + + c.JSON(http.StatusOK, gin.H{ + "code" : code, + "msg" : e.GetMsg(code), + "data" : make(map[string]string), + }) +} + +// 修改游戏 +func EditGame(c *gin.Context) { + id := com.StrTo(c.Param("id")).MustInt() + + name := c.PostForm("name") + cnName := c.PostForm("cn_name") + logo := c.PostForm("logo") + + valid := validation.Validation{} + + valid.Min(id, 1, "id").Message("ID必须大于0") + + code := e.INVALID_PARAMS + if ! valid.HasErrors() { + code = e.SUCCESS + if models.ExistGameById(id) { + data := make(map[string]interface{}) + if name != "" { + data["name"] = name + } + if cnName != "" { + data["cn_name"] = cnName + } + if logo != "" { + data["logo"] = logo + } + models.EditGame(id, data) + } else { + code = e.ERROR_NOT_EXIST_GAME + } + } + + c.JSON(http.StatusOK, gin.H{ + "code" : code, + "msg" : e.GetMsg(code), + "data" : make(map[string]string), + }) + +} + +// 删除游戏 +func DeleteGame(c *gin.Context) { + id := com.StrTo(c.Param("id")).MustInt() + + valid := validation.Validation{} + valid.Min(id, 1, "id").Message("ID必须大于0") + + code := e.INVALID_PARAMS + if ! valid.HasErrors() { + code = e.SUCCESS + if models.ExistGameById(id) { + models.DeleteGame(id) + } else { + code = e.ERROR_NOT_EXIST_GAME + } + } + + c.JSON(http.StatusOK, gin.H{ + "code" : code, + "msg" : e.GetMsg(code), + "data" : make(map[string]string), + }) +} \ No newline at end of file diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 0000000..c7c3cb2 --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,106 @@ +// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT +// This file was generated by swaggo/swag at +// 2019-08-03 00:06:46.857848 +0800 CST m=+0.026554074 + +package docs + +import ( + "bytes" + "encoding/json" + "strings" + + "github.com/alecthomas/template" + "github.com/swaggo/swag" +) + +var doc = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{.Description}}", + "title": "{{.Title}}", + "termsOfService": "http://swagger.io/terms/", + "contact": {}, + "license": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/v1/games": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "获取多个游戏", + "parameters": [ + { + "type": "string", + "description": "auth by /auth", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"code\" : 200, \"data\" : {}, msg: \"ok\"}", + "schema": { + "type": "string" + } + } + } + } + } + } +}` + +type swaggerInfo struct { + Version string + Host string + BasePath string + Schemes []string + Title string + Description string +} + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = swaggerInfo{ + Version: "1.0", + Host: "", + BasePath: "/api", + Schemes: []string{}, + Title: "GameDemo API", + Description: "This is a sample server Petstore server.", +} + +type s struct{} + +func (s *s) ReadDoc() string { + sInfo := SwaggerInfo + sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1) + + t, err := template.New("swagger_info").Funcs(template.FuncMap{ + "marshal": func(v interface{}) string { + a, _ := json.Marshal(v) + return string(a) + }, + }).Parse(doc) + if err != nil { + return doc + } + + var tpl bytes.Buffer + if err := t.Execute(&tpl, sInfo); err != nil { + return doc + } + + return tpl.String() +} + +func init() { + swag.Register(swag.Name, &s{}) +} diff --git a/docs/swagger.json b/docs/swagger.json new file mode 100644 index 0000000..62ff291 --- /dev/null +++ b/docs/swagger.json @@ -0,0 +1,42 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server.", + "title": "GameDemo API", + "termsOfService": "http://swagger.io/terms/", + "contact": {}, + "license": {}, + "version": "1.0" + }, + "basePath": "/api", + "paths": { + "/v1/games": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "获取多个游戏", + "parameters": [ + { + "type": "string", + "description": "auth by /auth", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"code\" : 200, \"data\" : {}, msg: \"ok\"}", + "schema": { + "type": "string" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 0000000..3ed4d82 --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,28 @@ +basePath: /api +info: + contact: {} + description: This is a sample server Petstore server. + license: {} + termsOfService: http://swagger.io/terms/ + title: GameDemo API + version: "1.0" +paths: + /v1/games: + get: + consumes: + - application/json + parameters: + - description: auth by /auth + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"code" : 200, "data" : {}, msg: "ok"}' + schema: + type: string + summary: 获取多个游戏 +swagger: "2.0" diff --git a/main.go b/main.go new file mode 100644 index 0000000..1dd0ab3 --- /dev/null +++ b/main.go @@ -0,0 +1,56 @@ +package main + +import ( + _ "./docs" + "context" + "fmt" + "game_demo/pkg/setting" + "game_demo/routers" + ginSwagger "github.com/swaggo/gin-swagger" + "github.com/swaggo/gin-swagger/swaggerFiles" + "log" + "net/http" + "os" + "os/signal" + "time" +) + +// @title GameDemo API +// @version 1.0 +// @description This is a sample server Petstore server. +// @termsOfService http://swagger.io/terms/ +// @BasePath /api + +func main() { + router := routers.InitRouter() + + router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) + + s := &http.Server{ + Addr: fmt.Sprintf(":%d", setting.HTTPPort), + Handler: router, + ReadTimeout: setting.ReadTimeout, + WriteTimeout: setting.WriteTimeout, + MaxHeaderBytes: 1 << 20, + } + + go func() { + if err := s.ListenAndServe(); err != nil { + log.Printf("Listen: %s\n", err) + } + }() + + quit := make(chan os.Signal) + signal.Notify(quit, os.Interrupt) + <- quit + + log.Printf("Shutdown Server") + + ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second) + defer cancel() + if err := s.Shutdown(ctx); err != nil { + log.Fatal("Server Shutdown:", err) + } + + log.Printf("Server exiting") +} \ No newline at end of file diff --git a/middleware/jwt/jwt.go b/middleware/jwt/jwt.go new file mode 100644 index 0000000..cbeba03 --- /dev/null +++ b/middleware/jwt/jwt.go @@ -0,0 +1,42 @@ +package jwt + +import ( + "game_demo/pkg/e" + "game_demo/pkg/util" + "github.com/gin-gonic/gin" + "net/http" + "time" +) + +func JWT() gin.HandlerFunc { + return func(c *gin.Context) { + var code int + var data interface{} + + code = e.SUCCESS + token := c.Request.Header.Get("Authorization") + if token == "" { + code = e.INVALID_PARAMS + } else { + claims, err := util.ParseToken(token) + if err != nil { + code = e.ERROR_AUTH_CHECK_TOKEN_FAIL + } else if time.Now().Unix() > claims.ExpiresAt { + code = e.ERROR_AUTH_CHECK_TOKEN_TIMEOUT + } + } + + if code != e.SUCCESS { + c.JSON(http.StatusUnauthorized, gin.H{ + "code" : code, + "msg" : e.GetMsg(code), + "data" : data, + }) + + c.Abort() + return + } + + c.Next() + } +} \ No newline at end of file diff --git a/models/auth.go b/models/auth.go new file mode 100644 index 0000000..583ceff --- /dev/null +++ b/models/auth.go @@ -0,0 +1,16 @@ +package models + +type Auth struct { + ID int `gorm:"primary_key" json:"id"` + Username string `json:"username"` + Password string `json:"password"` +} + +func CheckAuth(username, password string) bool { + var auth Auth + db.Select("id").Where(Auth{Username : username, Password : password}).First(&auth) + if auth.ID > 0 { + return true + } + return false +} \ No newline at end of file diff --git a/models/game.go b/models/game.go new file mode 100644 index 0000000..abaec6d --- /dev/null +++ b/models/game.go @@ -0,0 +1,60 @@ +package models + +type Game struct { + Model + + Name string `json:"name"` + CnName string `json:"cn_name"` + Logo string `json:"logo"` +} + +func GetGames(pageNum int, pageSize int, maps interface{}) (games []Game) { + db.Where(maps).Offset(pageNum).Limit(pageSize).Find(&games) + return +} + +func GetGameTotal(maps interface{}) (count int) { + db.Model(&Game{}).Where(maps).Count(&count) + + return +} + +func ExistGameByName(name string) bool { + var game Game + db.Select("id").Where("name = ?", name).First(&game) + if game.ID > 0 { + return true + } + return false +} + +func ExistGameById(id int) bool { + var game Game + db.Select("id").Where("id = ?", id).First(&game) + if game.ID > 0 { + return true + } + return false +} + +func AddGame(name string, cnName string, logo string) bool { + db.Create(&Game { + Name: name, + CnName: cnName, + Logo: logo, + }) + + return true +} + +func EditGame(id int, data interface{}) bool { + db.Model(&Game{}).Where("id = ?", id).Update(data) + + return true +} + +func DeleteGame(id int) bool { + db.Where("id = ?", id).Delete(&Game{}) + + return true +} \ No newline at end of file diff --git a/models/models.go b/models/models.go new file mode 100644 index 0000000..d536632 --- /dev/null +++ b/models/models.go @@ -0,0 +1,70 @@ +package models + +import ( + "fmt" + "game_demo/pkg/logging" + "game_demo/pkg/setting" + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/postgres" + "time" +) + +var db = InitDbConnect() + +type Model struct { + ID int `gorm:"primary_key" json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt *time.Time `sql:"index" json:"deleted_at"` +} + +func init() { + db.LogMode(true) + + db.SingularTable(true) + db.DB().SetMaxIdleConns(10) + db.DB().SetMaxOpenConns(100) + + DbMigrate() + //CloseDB() +} + +func InitDbConnect() *gorm.DB { + var ( + err error + dbType, dbName, user, password, host, port, tablePrefix string + ) + + sec, err := setting.Cfg.GetSection("database") + if err != nil { + logging.Info(2, "Fail to get section 'database': %v", err) + } + + dbType = sec.Key("TYPE").String() + dbName = sec.Key("NAME").String() + user = sec.Key("USER").String() + password = sec.Key("PASSWORD").String() + host = sec.Key("HOST").String() + port = sec.Key("PORT").String() + tablePrefix = sec.Key("TABLE_PREFIX").String() + + db, err := gorm.Open(dbType, fmt.Sprintf("host=%s user=%s dbname=%s port=%s sslmode=disable password=%s", host, user, dbName, port, password)) + + if err != nil { + logging.Info(err) + } + + gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string { + return tablePrefix + defaultTableName + } + + return db +} + +func DbMigrate() { + db.AutoMigrate(&Game{}, &Auth{}) +} + +func CloseDB() { + defer db.Close() +} \ No newline at end of file diff --git a/pkg/e/code.go b/pkg/e/code.go new file mode 100644 index 0000000..622b77b --- /dev/null +++ b/pkg/e/code.go @@ -0,0 +1,19 @@ +package e + +const ( + SUCCESS = 200 + ERROR = 500 + INVALID_PARAMS = 400 + + ERROR_EXIST_GAME = 10001 + ERROR_EXIST_TEAM = 10002 + ERROR_NOT_EXIST_GAME = 10003 + ERROR_NOT_EXIST_TEAM = 10004 + ERROR_NOT_EXIST_LEAGUE = 10005 + ERROR_NOT_EXIST_SERIES = 10006 + + ERROR_AUTH_CHECK_TOKEN_FAIL = 20001 + ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 20002 + ERROR_AUTH_TOKEN = 20003 + ERROR_AUTH = 20004 +) \ No newline at end of file diff --git a/pkg/e/msg.go b/pkg/e/msg.go new file mode 100644 index 0000000..529c8f2 --- /dev/null +++ b/pkg/e/msg.go @@ -0,0 +1,27 @@ +package e + +var MsgFlags = map[int]string { + SUCCESS : "ok", + ERROR : "fail", + INVALID_PARAMS : "请求参数错误", + ERROR_EXIST_GAME : "已存在该游戏", + ERROR_EXIST_TEAM : "已存在该队伍", + ERROR_NOT_EXIST_GAME : "该游戏不存在", + ERROR_NOT_EXIST_TEAM : "该队伍不存在", + ERROR_NOT_EXIST_LEAGUE : "该联赛不存在", + ERROR_NOT_EXIST_SERIES : "该系列赛不存在", + + ERROR_AUTH_CHECK_TOKEN_FAIL : "Token鉴权失败", + ERROR_AUTH_CHECK_TOKEN_TIMEOUT : "Token已超时", + ERROR_AUTH_TOKEN : "Token生成失败", + ERROR_AUTH : "Token错误", +} + +func GetMsg(code int) string { + msg, ok := MsgFlags[code] + if ok { + return msg + } + + return MsgFlags[ERROR] +} \ No newline at end of file diff --git a/pkg/logging/file.go b/pkg/logging/file.go new file mode 100644 index 0000000..6361ced --- /dev/null +++ b/pkg/logging/file.go @@ -0,0 +1,51 @@ +package logging + +import ( + "fmt" + "log" + "os" + "time" +) + +var ( + LogSavePath = "runtime/logs/" + LogSaveName = "log" + LogFileExt = "log" + TimeFormat = "20060102" +) + +func getLogFilePath() string { + return fmt.Sprintf("%s", LogSavePath) +} + +func getLogFileFullPath() string { + prefixPath := getLogFilePath() + suffixPath := fmt.Sprintf("%s%s.%s", LogSaveName, time.Now().Format(TimeFormat), LogFileExt) + + return fmt.Sprintf("%s%s", prefixPath, suffixPath) +} + +func openLogFile(filePath string) *os.File { + _, err := os.Stat(filePath) + switch { + case os.IsNotExist(err): + mkDir() + case os.IsPermission(err): + log.Fatalf("Permission :%v", err) + } + + handle, err := os.OpenFile(filePath, os.O_APPEND | os.O_CREATE | os.O_WRONLY, 0644) + if err != nil { + log.Fatalf("Fail to OpenFile :%v", err) + } + + return handle +} + +func mkDir() { + dir, _ := os.Getwd() + err := os.MkdirAll(dir + "/" + getLogFilePath(), os.ModePerm) + if err != nil { + panic(err) + } +} diff --git a/pkg/logging/log.go b/pkg/logging/log.go new file mode 100644 index 0000000..23f3564 --- /dev/null +++ b/pkg/logging/log.go @@ -0,0 +1,73 @@ +package logging + +import ( + "fmt" + "log" + "os" + "path/filepath" + "runtime" +) + +type Level int + +var ( + F *os.File + + DefaultPrefix = "" + DefaultCallerDepth = 2 + + logger *log.Logger + logPrefix = "" + levelFlags = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"} +) + +const ( + DEBUG Level = iota + INFO + WARNING + ERROR + FATAL +) + +func init() { + filePath := getLogFileFullPath() + F = openLogFile(filePath) + + logger = log.New(F, DefaultPrefix, log.LstdFlags) +} + +func Debug(v ...interface{}) { + setPrefix(DEBUG) + logger.Println(v) +} + +func Info(v ...interface{}) { + setPrefix(INFO) + logger.Println(v) +} + +func Warn(v ...interface{}) { + setPrefix(WARNING) + logger.Println(v) +} + +func Error(v ...interface{}) { + setPrefix(ERROR) + logger.Println(v) +} + +func Fatal(v ...interface{}) { + setPrefix(FATAL) + logger.Fatalln(v) +} + +func setPrefix(level Level) { + _, file, line, ok := runtime.Caller(DefaultCallerDepth) + if ok { + logPrefix = fmt.Sprintf("[%s][%s:%d]", levelFlags[level], filepath.Base(file), line) + } else { + logPrefix = fmt.Sprintf("[%s]", levelFlags[level]) + } + + logger.SetPrefix(logPrefix) +} \ No newline at end of file diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go new file mode 100644 index 0000000..4fab85c --- /dev/null +++ b/pkg/setting/setting.go @@ -0,0 +1,59 @@ +package setting + +import ( + "gopkg.in/ini.v1" + "log" + "time" +) + +var ( + Cfg *ini.File + + RunMode string + + HTTPPort int + ReadTimeout time.Duration + WriteTimeout time.Duration + + PageSize int + JwtSecret string +) + +func init() { + var err error + Cfg, err = ini.Load("conf/app.ini") + if err != nil { + log.Fatal("Fail to parse 'conf/app.ini': %v", err) + } + + LoadBase() + LoadServer() + LoadApp() +} + +func LoadBase() { + RunMode = Cfg.Section("").Key("RUN_MODE").MustString("debug") +} + +func LoadServer() { + sec, err := Cfg.GetSection("server") + if err != nil { + log.Fatalf("Fail to get section 'server': %v", err) + } + + RunMode = Cfg.Section("").Key("RUN_MODE").MustString("debug") + + HTTPPort = sec.Key("HTTP_PORT").MustInt(8000) + ReadTimeout = time.Duration(sec.Key("READ_TIMEOUT").MustInt(60)) * time.Second + WriteTimeout = time.Duration(sec.Key("WRITE_TIMEOUT").MustInt(60)) * time.Second +} + +func LoadApp() { + sec, err := Cfg.GetSection("app") + if err != nil { + log.Fatalf("Fail to get section 'app': %v", err) + } + + JwtSecret = sec.Key("JWT_SECRET").MustString("!@)*#)!@U#@*!@!)") + PageSize = sec.Key("PAGE_SIZE").MustInt(10) +} \ No newline at end of file diff --git a/pkg/util/jwt.go b/pkg/util/jwt.go new file mode 100644 index 0000000..91ed6f9 --- /dev/null +++ b/pkg/util/jwt.go @@ -0,0 +1,48 @@ +package util + +import ( + "game_demo/pkg/setting" + "github.com/dgrijalva/jwt-go" + "time" +) + +var jwtSecret = []byte(setting.JwtSecret) + +type Claims struct { + Username string `json:"username"` + Password string `json:"password"` + jwt.StandardClaims +} + +func GenerateToken(username, password string) (string, error) { + nowTime := time.Now() + expireTime := nowTime.Add(3 * time.Hour) + + claims := Claims{ + Username: username, + Password: password, + StandardClaims: jwt.StandardClaims{ + ExpiresAt: expireTime.Unix(), + Issuer : "gin-game-demo", + }, + } + + tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + token, err := tokenClaims.SignedString(jwtSecret) + + return token, err +} + +func ParseToken(token string) (*Claims, error) { + tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) { + return jwtSecret, nil + }) + + if tokenClaims != nil { + if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid { + return claims, nil + } + } + + return nil, err +} \ No newline at end of file diff --git a/pkg/util/pagination.go b/pkg/util/pagination.go new file mode 100644 index 0000000..23819ad --- /dev/null +++ b/pkg/util/pagination.go @@ -0,0 +1,18 @@ +package util + +import ( + "game_demo/pkg/setting" + "github.com/Unknwon/com" + + "github.com/gin-gonic/gin" +) + +func GetPage(c *gin.Context) int { + result := 0 + page, _ := com.StrTo(c.Query("page")).Int() + if page > 0 { + result = (page - 1) * setting.PageSize + } + + return result +} \ No newline at end of file diff --git a/routers/router.go b/routers/router.go new file mode 100644 index 0000000..3001877 --- /dev/null +++ b/routers/router.go @@ -0,0 +1,25 @@ +package routers + +import ( + "game_demo/api" + "game_demo/pkg/setting" + "game_demo/routers/v1" + "github.com/gin-gonic/gin" +) + +func InitRouter() *gin.Engine { + r := gin.New() + + r.Use(gin.Logger()) + + r.Use(gin.Recovery()) + + gin.SetMode(setting.RunMode) + + r.GET("/auth", api.GetAuth) + + // 加载模块的路由 + v1.GameRouter(r) + + return r +} \ No newline at end of file diff --git a/routers/v1/game.go b/routers/v1/game.go new file mode 100644 index 0000000..412cb87 --- /dev/null +++ b/routers/v1/game.go @@ -0,0 +1,23 @@ +package v1 + +import ( + "game_demo/api/v1" + "game_demo/middleware/jwt" + "github.com/gin-gonic/gin" +) + +func GameRouter(r *gin.Engine) { + apiv1 := r.Group("../api/v1") + apiv1.Use(jwt.JWT()) + { + // 获取游戏列表 + apiv1.GET("/games", v1.GetGames) + // 新建游戏 + apiv1.POST("/games", v1.AddGame) + // 更新指定游戏 + apiv1.PUT("/games/:id", v1.EditGame) + // 删除指定游戏 + apiv1.DELETE("/games/:id", v1.DeleteGame) + } + +} \ No newline at end of file diff --git a/runtime/logs/log20190802.log b/runtime/logs/log20190802.log new file mode 100644 index 0000000..82a3c5c --- /dev/null +++ b/runtime/logs/log20190802.log @@ -0,0 +1,40 @@ +[INFO][auth.go:26]2019/08/02 19:59:10 [] +[INFO][auth.go:26]2019/08/02 19:59:27 [] +[INFO][auth.go:26]2019/08/02 19:59:29 [] +[INFO][auth.go:26]2019/08/02 19:59:38 [] +[INFO][auth.go:46]2019/08/02 19:59:38 [Password.Required Can not be empty] +[INFO][auth.go:26]2019/08/02 19:59:45 [] +[INFO][auth.go:46]2019/08/02 19:59:45 [Username.Required Can not be empty] +[INFO][auth.go:46]2019/08/02 19:59:45 [Password.Required Can not be empty] +[INFO][models.go:54]2019/08/02 20:01:16 [dial tcp 127.0.0.1:543: connect: connection refused] +[INFO][main.go:36]2019/08/02 20:18:21 [Shutdown Server] +[INFO][main.go:44]2019/08/02 20:18:21 [Server exiting] +[INFO][main.go:36]2019/08/02 20:22:44 [Shutdown Server] +[INFO][main.go:44]2019/08/02 20:22:44 [Server exiting] +[INFO][main.go:28]2019/08/02 20:22:44 [Listen: %s + http: Server closed] +[INFO][main.go:36]2019/08/02 20:22:53 [Shutdown Server] +[INFO][main.go:44]2019/08/02 20:22:53 [Server exiting] +[INFO][main.go:28]2019/08/02 20:22:53 [Listen: %s + http: Server closed] +[INFO][main.go:36]2019/08/02 20:23:35 [Shutdown Server] +[INFO][main.go:28]2019/08/02 20:23:35 [Listen: %s + http: Server closed] +[INFO][main.go:44]2019/08/02 20:23:35 [Server exiting] +[INFO][main.go:36]2019/08/02 20:23:38 [Shutdown Server] +[INFO][main.go:44]2019/08/02 20:23:38 [Server exiting] +[INFO][main.go:28]2019/08/02 20:23:38 [Listen: %s + http: Server closed] +[INFO][main.go:36]2019/08/02 20:28:25 [Shutdown Server] +[INFO][main.go:44]2019/08/02 20:28:25 [Server exiting] +[INFO][main.go:28]2019/08/02 20:28:25 [Listen: %s + http: Server closed] +[INFO][main.go:36]2019/08/02 20:28:31 [Shutdown Server] +[INFO][main.go:44]2019/08/02 20:28:31 [Server exiting] +[INFO][main.go:36]2019/08/02 20:28:49 [Shutdown Server] +[INFO][main.go:44]2019/08/02 20:28:49 [Server exiting] +[INFO][main.go:37]2019/08/02 20:28:59 [Shutdown Server] +[INFO][main.go:45]2019/08/02 20:28:59 [Server exiting] +[INFO][auth.go:26]2019/08/02 23:42:23 [] +[INFO][auth.go:46]2019/08/02 23:42:23 [Username.Required Can not be empty] +[INFO][auth.go:46]2019/08/02 23:42:23 [Password.Required Can not be empty] diff --git a/runtime/logs/log20190803.log b/runtime/logs/log20190803.log new file mode 100644 index 0000000..f6a2614 --- /dev/null +++ b/runtime/logs/log20190803.log @@ -0,0 +1 @@ +[INFO][auth.go:26]2019/08/03 00:19:40 []