liujiandao
2023-11-22 3b474a056ea35196ce5d2a5f297eafa11295a95d
添加登录功能
11个文件已添加
3个文件已修改
681 ■■■■■ 已修改文件
constvar/const.go 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
go.mod 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
go.sum 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
initialize/router.go 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
middleware/jwt.go 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
middleware/utils.go 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/contextx/contextx.go 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/ecode/code.go 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/ecode/msg.go 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/logx/console.go 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/logx/index.go 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/logx/json.go 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/logx/service.go 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
request/jwt.go 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
constvar/const.go
New file
@@ -0,0 +1,9 @@
package constvar
type UserType int
const (
    UserTypeSuper   UserType = iota + 1 // 超级管理员
    UserTypePrimary                     // 主账户
    UserTypeSub                         // 子账户
)
go.mod
@@ -3,11 +3,13 @@
go 1.18
require (
    github.com/dgrijalva/jwt-go v3.2.0+incompatible
    github.com/fsnotify/fsnotify v1.6.0
    github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
    github.com/gin-gonic/gin v1.9.1
    github.com/glebarez/sqlite v1.8.0
    github.com/go-sql-driver/mysql v1.7.1
    github.com/golang-jwt/jwt/v4 v4.5.0
    github.com/h2non/filetype v1.1.3
    github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
    github.com/redis/go-redis/v9 v9.0.5
@@ -23,6 +25,7 @@
    golang.org/x/sync v0.3.0
    google.golang.org/grpc v1.55.0
    google.golang.org/protobuf v1.30.0
    gopkg.in/natefinch/lumberjack.v2 v2.2.1
    gorm.io/driver/mysql v1.5.1
    gorm.io/driver/postgres v1.5.2
    gorm.io/driver/sqlserver v1.5.1
go.sum
@@ -45,7 +45,6 @@
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -73,6 +72,8 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
@@ -140,10 +141,10 @@
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
@@ -226,16 +227,10 @@
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8=
github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
@@ -769,6 +764,8 @@
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
initialize/router.go
@@ -2,6 +2,7 @@
import (
    "net/http"
    "srm/middleware"
    "srm/router/purchase"
    "github.com/gin-gonic/gin"
@@ -25,16 +26,15 @@
    global.GVA_LOG.Info("register swagger handler")
    // 方便统一添加路由组前缀 多服务器上线使用
    PublicGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
    PrivateGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
    {
        // 健康监测
        PublicGroup.GET("/health", func(c *gin.Context) {
        PrivateGroup.GET("/health", func(c *gin.Context) {
            c.JSON(http.StatusOK, "ok")
        })
        //systemRouter.InitInitRouter(PublicGroup) // 自动初始化相关
    }
    PrivateGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
    PrivateGroup.Use(middleware.JWTAuth())
    {
        systemRouter.InitSystemRouter(PrivateGroup) // system相关路由
        //exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
middleware/jwt.go
New file
@@ -0,0 +1,39 @@
package middleware
import (
    "github.com/gin-gonic/gin"
    "srm/pkg/contextx"
    "srm/pkg/ecode"
    "strings"
)
func JWTAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx := new(contextx.Context).SetCtx(c)
        // 我们这里jwt鉴权取头部信息 Authorization 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
        token := c.Request.Header.Get("Authorization")
        if token == "" {
            ctx.Fail(ecode.JWTEmpty)
            c.Abort()
            return
        }
        slices := strings.Split(token, " ")
        if len(slices) == 2 {
            token = slices[1]
        }
        j := NewJWT()
        // parseToken 解析token包含的信息
        claims, err := j.ParseToken(token)
        if err != nil {
            if err == TokenExpired {
                c.Next()
                return
            }
            c.Next()
            return
        }
        c.Set("claims", claims)
        c.Next()
    }
}
middleware/utils.go
New file
@@ -0,0 +1,60 @@
package middleware
import (
    "errors"
    "github.com/golang-jwt/jwt/v4"
    "srm/request"
)
type JWT struct {
    SigningKey []byte
}
var (
    TokenExpired     = errors.New("Token is expired")
    TokenNotValidYet = errors.New("Token not active yet")
    TokenMalformed   = errors.New("That's not even a token")
    TokenInvalid     = errors.New("Couldn't handle this token:")
)
func NewJWT() *JWT {
    return &JWT{
        []byte("327a9457-899a-481e-8b30-58cc97e5b808"),
    }
}
// CreateToken 创建一个token
func (j *JWT) CreateToken(claims request.CustomClaims) (string, error) {
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(j.SigningKey)
}
// ParseToken 解析token
func (j *JWT) ParseToken(tokenString string) (*request.CustomClaims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &request.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.(*request.CustomClaims); ok && token.Valid {
            return claims, nil
        }
        return nil, TokenInvalid
    } else {
        return nil, TokenInvalid
    }
}
pkg/contextx/contextx.go
New file
@@ -0,0 +1,97 @@
package contextx
import (
    "github.com/gin-gonic/gin"
    "net/http"
    "srm/pkg/ecode"
    "srm/pkg/logx"
)
type (
    Context struct {
        ctx       *gin.Context
        paramsMap map[string]interface{}
    }
    Response struct {
        Code int         `json:"code"`
        Data interface{} `json:"data"`
        Msg  string      `json:"msg"`
    }
)
func NewContext(ctx *gin.Context, params interface{}) (r *Context, isAllow bool) {
    r = &Context{
        ctx: ctx,
    }
    if r.ctx.Request.Method == "OPTIONS" {
        r.ctx.String(http.StatusOK, "")
        return
    }
    defer func() {
        query := r.ctx.Request.URL.RawQuery
        if query != "" {
            query = "?" + query
        }
        urlPath := r.ctx.Request.URL.Path
        logx.Infof("%s | %s %s | uid: %s | %+v", ctx.ClientIP(), r.ctx.Request.Method, urlPath+query, r.GetUserId(), params)
    }()
    // validate params
    if params != nil {
        if err := r.ctx.ShouldBind(params); err != nil {
            r.Fail(ecode.ParamsErr)
            return
        }
    }
    isAllow = true
    return
}
func (slf *Context) GetRequestPath() (r string) {
    r = slf.ctx.Request.URL.Path
    return
}
func (slf *Context) GetUserId() (r string) {
    v := slf.paramsMap["userId"]
    switch v.(type) {
    case string:
        r = v.(string)
    }
    return
}
func (slf *Context) Result(code int, data interface{}, msg string) {
    slf.ctx.JSON(http.StatusOK, Response{
        Code: code,
        Data: data,
        Msg:  msg,
    })
}
func (slf *Context) Ok() {
    slf.Result(ecode.OK, map[string]interface{}{}, "")
}
func (slf *Context) OkWithDetailed(data interface{}) {
    slf.Result(ecode.OK, data, "")
}
func (slf *Context) Fail(errCode int) {
    slf.Result(errCode, map[string]interface{}{}, ecode.GetMsg(errCode))
}
func (slf *Context) FailWithDetailed(errCode int, data interface{}) {
    slf.Result(errCode, data, ecode.GetMsg(errCode))
}
func (slf *Context) GetCtx() *gin.Context {
    return slf.ctx
}
func (slf *Context) SetCtx(c *gin.Context) *Context {
    slf.ctx = c
    return slf
}
pkg/ecode/code.go
New file
@@ -0,0 +1,21 @@
package ecode
const (
    OK = 200
    UnknownErr            = 2001 // 未知错误
    DBErr                 = 2002 // db错误
    RedisErr              = 2003 // redis错误
    ParamsErr             = 2004 // 请求参数错误
    UserNotExist          = 2005 // 用户不存在
    PasswordErr           = 2006 // 密码错误
    UserForbidden         = 2007 // 用户被禁用
    CaptchaGenerateFailed = 2008 // 验证码生成失败
    CaptchaErr            = 2009 // 验证码错误
    CreateTokenErr        = 2010 // 创建token失败
    JWTInBlackList        = 2011 // JWT在白名单
    JWTDisabled           = 2012 // JWT失效
    JWTEmpty              = 2013 // JWT为空
    JWTExpire             = 2014 // JWT过期
    JWTParseErr           = 2015 // JWT解析失败
)
pkg/ecode/msg.go
New file
@@ -0,0 +1,24 @@
package ecode
var MsgFlags = map[int]string{
    UnknownErr:            "未知错误",
    DBErr:                 "db错误",
    RedisErr:              "redis错误",
    ParamsErr:             "请求参数错误",
    UserNotExist:          "用户不存在",
    PasswordErr:           "密码错误",
    UserForbidden:         "用户被禁用",
    CaptchaGenerateFailed: "验证码生成失败",
    CaptchaErr:            "验证码错误",
    CreateTokenErr:        "创建token失败",
    JWTInBlackList:        "JWT在白名单",
    JWTDisabled:           "JWT失效",
    JWTEmpty:              "JWT为空",
    JWTExpire:             "JWT过期",
    JWTParseErr:           "JWT解析失败",
}
func GetMsg(errCode int) (errMsg string) {
    errMsg, _ = MsgFlags[errCode]
    return
}
pkg/logx/console.go
New file
@@ -0,0 +1,89 @@
package logx
import (
    "fmt"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "time"
)
type ConsoleLog struct {
    val string
}
func NewConsoleLog() Encoder {
    return new(ConsoleLog)
}
// Config 自定义配置.
func (slf *ConsoleLog) Config() zapcore.Encoder {
    var (
        cfg = zap.NewProductionEncoderConfig()
    )
    // 时间格式自定义
    cfg.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
        enc.AppendString("[" + t.Format("2006-01-02 15:04:05") + "]")
    }
    // 打印路径自定义
    cfg.EncodeCaller = func(caller zapcore.EntryCaller, encoder zapcore.PrimitiveArrayEncoder) {
        encoder.AppendString("[" + getFilePath(caller) + "]")
    }
    // 级别显示自定义
    cfg.EncodeLevel = func(level zapcore.Level, encoder zapcore.PrimitiveArrayEncoder) {
        encoder.AppendString("[" + level.String() + "]")
    }
    return zapcore.NewConsoleEncoder(cfg)
}
// WithKey 添加单个键.
func (slf *ConsoleLog) WithKey(key string) Encoder {
    slf.val = slf.val + "[" + key + "]    "
    return slf
}
// WithField 添加字段.
func (slf *ConsoleLog) WithField(key, val string) Encoder {
    slf.val = slf.val + fmt.Sprintf("[%s:%s]    ", key, val)
    return slf
}
func (slf *ConsoleLog) Debug(msg string) {
    _logger.Debug(slf.val + msg)
}
func (slf *ConsoleLog) Debugf(format string, v ...interface{}) {
    _logger.Debug(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Info(msg string) {
    _logger.Info(slf.val + msg)
}
func (slf *ConsoleLog) Infof(format string, v ...interface{}) {
    _logger.Info(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Warn(msg string) {
    _logger.Warn(slf.val + msg)
}
func (slf *ConsoleLog) Warnf(format string, v ...interface{}) {
    _logger.Warn(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Error(msg string) {
    _logger.Error(slf.val + msg)
}
func (slf *ConsoleLog) Errorf(format string, v ...interface{}) {
    _logger.Error(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Fatal(msg string) {
    _logger.Fatal(slf.val + msg)
}
func (slf *ConsoleLog) Fatalf(format string, v ...interface{}) {
    _logger.Fatal(fmt.Sprintf(slf.val+format, v...))
}
pkg/logx/index.go
New file
@@ -0,0 +1,158 @@
package logx
import (
    "go.uber.org/zap"
    "go.uber.org/zap/buffer"
    "go.uber.org/zap/zapcore"
    "gopkg.in/natefinch/lumberjack.v2"
    "os"
    "path"
    "strings"
)
type (
    Conf struct {
        Path    string // 日志路径
        Encoder string // 编码器选择
    }
    logItem struct {
        FileName string
        Level    zap.LevelEnablerFunc
    }
    Encoder interface {
        Config() zapcore.Encoder
        WithKey(key string) Encoder
        WithField(key, val string) Encoder
        Debug(msg string)
        Debugf(format string, v ...interface{})
        Info(msg string)
        Infof(format string, v ...interface{})
        Warn(msg string)
        Warnf(format string, v ...interface{})
        Error(msg string)
        Errorf(format string, v ...interface{})
        Fatal(msg string)
        Fatalf(format string, v ...interface{})
    }
)
var (
    maxSize    = 200 // 每个日志文件最大尺寸200M
    maxBackups = 20  // 日志文件最多保存20个备份
    maxAge     = 30  // 保留最大天数
    _logger    *zap.Logger
    _pool      = buffer.NewPool()
    c          Conf
    ConsoleEncoder = "console" // 控制台输出
    JsonEncoder    = "json"    // json输出
)
// Init 初始化日志.
func Init(conf Conf) {
    c = conf
    prefix, suffix := getFileSuffixPrefix(c.Path)
    infoPath := path.Join(prefix + ".info" + suffix)
    errPath := path.Join(prefix + ".err" + suffix)
    items := []logItem{
        {
            FileName: infoPath,
            Level: func(level zapcore.Level) bool {
                return level <= zap.InfoLevel
            },
        },
        {
            FileName: errPath,
            Level: func(level zapcore.Level) bool {
                return level > zap.InfoLevel
            },
        },
    }
    NewLogger(items)
}
// NewLogger 日志.
func NewLogger(items []logItem) {
    var (
        cfg   zapcore.Encoder
        cores []zapcore.Core
    )
    switch c.Encoder {
    case JsonEncoder:
        cfg = NewJsonLog().Config()
    case ConsoleEncoder:
        cfg = NewConsoleLog().Config()
    default:
        cfg = NewConsoleLog().Config()
    }
    for _, v := range items {
        hook := lumberjack.Logger{
            Filename:   v.FileName,
            MaxSize:    maxSize,    // 每个日志文件保存的最大尺寸 单位:M
            MaxBackups: maxBackups, // 日志文件最多保存多少个备份
            MaxAge:     maxAge,     // 文件最多保存多少天
            Compress:   true,       // 是否压缩
            LocalTime:  true,       // 备份文件名本地/UTC时间
        }
        core := zapcore.NewCore(
            cfg, // 编码器配置;
            zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)), // 打印到控制台和文件
            v.Level, // 日志级别
        )
        cores = append(cores, core)
    }
    // 开启开发模式,堆栈跟踪
    caller := zap.AddCaller()
    // 开发模式
    development := zap.Development()
    // 二次封装
    skip := zap.AddCallerSkip(1)
    // 构造日志
    _logger = zap.New(zapcore.NewTee(cores...), caller, development, skip)
    return
}
// GetEncoder 获取自定义编码器.
func GetEncoder() Encoder {
    switch c.Encoder {
    case JsonEncoder:
        return NewJsonLog()
    case ConsoleEncoder:
        return NewConsoleLog()
    default:
        return NewConsoleLog()
    }
}
// GetLogger 获取日志记录器.
func GetLogger() *zap.Logger {
    return _logger
}
// getFileSuffixPrefix 文件路径切割
func getFileSuffixPrefix(fileName string) (prefix, suffix string) {
    paths, _ := path.Split(fileName)
    base := path.Base(fileName)
    suffix = path.Ext(fileName)
    prefix = strings.TrimSuffix(base, suffix)
    prefix = path.Join(paths, prefix)
    return
}
// getFilePath 自定义获取文件路径.
func getFilePath(ec zapcore.EntryCaller) string {
    if !ec.Defined {
        return "undefined"
    }
    buf := _pool.Get()
    buf.AppendString(ec.Function)
    buf.AppendByte(':')
    buf.AppendInt(int64(ec.Line))
    caller := buf.String()
    buf.Free()
    return caller
}
pkg/logx/json.go
New file
@@ -0,0 +1,91 @@
package logx
import (
    "fmt"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "time"
)
type JsonLog struct {
    fields []zap.Field
    val    string
}
// NewJsonLog  自定义添加log field.
func NewJsonLog() Encoder {
    return &JsonLog{fields: make([]zap.Field, 0)}
}
// Config 自定义配置.
func (slf *JsonLog) Config() zapcore.Encoder {
    var (
        cfg = zap.NewProductionEncoderConfig()
    )
    // 时间格式自定义
    cfg.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
        enc.AppendString(t.Format("2006-01-02 15:04:05"))
    }
    // 打印路径自定义
    cfg.EncodeCaller = func(caller zapcore.EntryCaller, encoder zapcore.PrimitiveArrayEncoder) {
        encoder.AppendString(getFilePath(caller))
    }
    // 级别显示自定义
    cfg.EncodeLevel = func(level zapcore.Level, encoder zapcore.PrimitiveArrayEncoder) {
        encoder.AppendString(level.String())
    }
    return zapcore.NewJSONEncoder(cfg)
}
// WithKey 添加单个键.
func (slf *JsonLog) WithKey(key string) Encoder {
    slf.val = slf.val + key + " "
    return slf
}
// WithField 添加字段.
func (slf *JsonLog) WithField(key, val string) Encoder {
    slf.fields = append(slf.fields, zap.String(key, val))
    return slf
}
func (slf *JsonLog) Debug(msg string) {
    _logger.Debug(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Debugf(format string, v ...interface{}) {
    _logger.Debug(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Info(msg string) {
    _logger.Info(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Infof(format string, v ...interface{}) {
    _logger.Info(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Warn(msg string) {
    _logger.Warn(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Warnf(format string, v ...interface{}) {
    _logger.Warn(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Error(msg string) {
    _logger.Error(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Errorf(format string, v ...interface{}) {
    _logger.Error(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Fatal(msg string) {
    _logger.Fatal(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Fatalf(format string, v ...interface{}) {
    _logger.Fatal(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
pkg/logx/service.go
New file
@@ -0,0 +1,49 @@
package logx
import (
    "fmt"
)
func Sync() {
    _ = _logger.Sync()
}
func Debug(msg string) {
    _logger.Debug(msg)
}
func Debugf(format string, v ...interface{}) {
    _logger.Debug(fmt.Sprintf(format, v...))
}
func Info(msg string) {
    _logger.Info(msg)
}
func Infof(format string, v ...interface{}) {
    _logger.Info(fmt.Sprintf(format, v...))
}
func Warn(msg string) {
    _logger.Warn(msg)
}
func Warnf(format string, v ...interface{}) {
    _logger.Warn(fmt.Sprintf(format, v...))
}
func Error(msg string) {
    _logger.Error(msg)
}
func Errorf(format string, v ...interface{}) {
    _logger.Error(fmt.Sprintf(format, v...))
}
func Fatal(msg string) {
    _logger.Fatal(msg)
}
func Fatalf(format string, v ...interface{}) {
    _logger.Fatal(fmt.Sprintf(format, v...))
}
request/jwt.go
New file
@@ -0,0 +1,20 @@
package request
import (
    "github.com/dgrijalva/jwt-go"
    "srm/constvar"
)
// Custom claims structure
type CustomClaims struct {
    BaseClaims
    BufferTime int64
    jwt.StandardClaims
}
type BaseClaims struct {
    UserId   string
    Username string
    ParentId string
    UserType constvar.UserType
}