commit d609cc25d9c04005fd0905de58d860cac2772877 Author: Stanly Date: Sat Jul 4 07:23:02 2020 +0800 Initial commit diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..e584339 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,125 @@ +linters-settings: + depguard: + list-type: blacklist + packages: + # logging is allowed only by logutils.Log, logrus + # is allowed to use only in logutils package + - github.com/sirupsen/logrus + packages-with-error-message: + - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" + dupl: + threshold: 100 + funlen: + lines: 100 + statements: 50 + goconst: + min-len: 2 + min-occurrences: 2 + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + disabled-checks: + - dupImport # https://github.com/go-critic/go-critic/issues/845 + - ifElseChain + - octalLiteral + - whyNoLint + - wrapperFunc + gocyclo: + min-complexity: 15 + goimports: + local-prefixes: github.com/golangci/golangci-lint + golint: + min-confidence: 0 + gomnd: + settings: + mnd: + # don't include the "operation" and "assign" + checks: argument,case,condition,return + govet: + check-shadowing: true + settings: + printf: + funcs: + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf + lll: + line-length: 140 + maligned: + suggest-new: true + misspell: + locale: US + +linters: + # please, do not use `enable-all`: it's deprecated and will be removed soon. + # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint + disable-all: true + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - dupl + - errcheck + - funlen + - gochecknoinits + - goconst + - gocritic + - gocyclo + - gofmt + - goimports + - golint + - gomnd + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - interfacer + - lll + - misspell + - nakedret + - rowserrcheck + - scopelint + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck + - whitespace + + # don't enable: + # - gochecknoglobals + # - gocognit + # - godox + # - maligned + # - prealloc + +issues: + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + - path: _test\.go + linters: + - gomnd + +run: + skip-dirs: + - test/testdata_etc + - internal/cache + - internal/renameio + - internal/robustio + +# golangci.com configuration +# https://github.com/golangci/golangci/wiki/Configuration +service: + golangci-lint-version: 1.23.x # use the fixed version to not introduce new linters unexpectedly + prepare: + - echo "here I can run custom commands, but no preparation needed for this repo" diff --git a/captcha/captcha.go b/captcha/captcha.go new file mode 100644 index 0000000..f8604ab --- /dev/null +++ b/captcha/captcha.go @@ -0,0 +1,14 @@ +package captcha + +import ( + "github.com/mojocn/base64Captcha" +) + +func Generate() (string, string, error) { + driver := base64Captcha.NewDriverDigit(80, 160, 4, 0.1, 4) + return base64Captcha.NewCaptcha(driver, base64Captcha.DefaultMemStore).Generate() +} + +func Verify(id, code string) bool { + return base64Captcha.DefaultMemStore.Verify(id, code, true) +} diff --git a/crypto/crypto.go b/crypto/crypto.go new file mode 100644 index 0000000..f9a17af --- /dev/null +++ b/crypto/crypto.go @@ -0,0 +1,52 @@ +package crypto + +import ( + "crypto/sha1" + "fmt" + + "github.com/Luzifer/go-openssl/v3" + "golang.org/x/crypto/bcrypt" +) + +// SHA1 回傳sha1加密 +func SHA1(v string) string { + h := sha1.New() + h.Write([]byte(v)) + bs := h.Sum(nil) + return fmt.Sprintf("%x", bs) +} + +func EncryptPassword(pwd string) (string, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost) + if err != nil { + return "", err + } + + return string(hash), nil +} + +func CheckPassword(pwd, hash string) error { + return bcrypt.CompareHashAndPassword([]byte(hash), []byte(pwd)) +} + +func DecryptAES(value, key string) ([]byte, error) { + o := openssl.New() + + dec, err := o.DecryptBytes(key, []byte(value), openssl.DigestMD5Sum) + if err != nil { + return nil, err + } + + return dec, nil +} + +func EncryptAES(value, key string) ([]byte, error) { + o := openssl.New() + + enc, err := o.EncryptBytes(key, []byte(value), openssl.DigestMD5Sum) + if err != nil { + return nil, err + } + + return enc, nil +} diff --git a/generate/generate.go b/generate/generate.go new file mode 100644 index 0000000..b6cdbf8 --- /dev/null +++ b/generate/generate.go @@ -0,0 +1,34 @@ +package generate + +import ( + "crypto/rand" + "math/big" +) + +// GetRandomString 取得隨機字串 +func GetRandomString(n int) (string, error) { + const alphaNum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + + buffer := make([]byte, n) + max := big.NewInt(int64(len(alphaNum))) + + for i := 0; i < n; i++ { + index, err := randomInt(max) + if err != nil { + return "", err + } + + buffer[i] = alphaNum[index] + } + + return string(buffer), nil +} + +func randomInt(max *big.Int) (int, error) { + random, err := rand.Int(rand.Reader, max) + if err != nil { + return 0, err + } + + return int(random.Int64()), nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7e79c24 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module git.simts.cc/common/lib + +go 1.14 + +require ( + github.com/Luzifer/go-openssl/v3 v3.1.0 + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/mojocn/base64Captcha v1.3.1 + go.uber.org/zap v1.15.0 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..eadcfbb --- /dev/null +++ b/go.sum @@ -0,0 +1,68 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Luzifer/go-openssl/v3 v3.1.0 h1:QqKqo6kYXGGUsvtUoCpRZm8lHw+jDfhbzr36gVj+/gw= +github.com/Luzifer/go-openssl/v3 v3.1.0/go.mod h1:liy3FXuuS8hfDlYh1T+l78AwQ/NjZflJz0NDvjKhwDs= +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/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mojocn/base64Captcha v1.3.1 h1:2Wbkt8Oc8qjmNJ5GyOfSo4tgVQPsbKMftqASnq8GlT0= +github.com/mojocn/base64Captcha v1.3.1/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/image v0.0.0-20190501045829-6d32002ffd75 h1:TbGuee8sSq15Iguxu4deQ7+Bqq/d2rsQejGcEtADAMQ= +golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/image/image.go b/image/image.go new file mode 100644 index 0000000..dacd27b --- /dev/null +++ b/image/image.go @@ -0,0 +1,89 @@ +package image + +import ( + "bytes" + "image/gif" + "image/jpeg" + "image/png" + "io" + "io/ioutil" + "mime/multipart" + "os" +) + +func CompressImage(input interface{}, output string) (err error) { + var file io.Reader + switch input.(type) { + case string: + if file, err = os.Open(input.(string)); err != nil { + return err + } + defer file.(*os.File).Close() + defer os.Remove(input.(string)) + + case *multipart.FileHeader: + fileHeader := input.(*multipart.FileHeader) + file, err = fileHeader.Open() + if err != nil { + return err + } + defer file.(multipart.File).Close() + } + + bs, err := ioutil.ReadAll(file) + if err != nil { + return err + } + + var ext string + if _, err = jpeg.Decode(bytes.NewReader(bs)); err == nil { + ext = "jpg" + } else if _, err = png.Decode(bytes.NewReader(bs)); err == nil { + ext = "png" + } else if _, err = gif.Decode(bytes.NewReader(bs)); err == nil { + ext = "gif" + } + + outputFile, err := os.Create(output) + if err != nil { + return err + } + defer outputFile.Close() + + switch ext { + case "jpg": + img, err := jpeg.Decode(bytes.NewReader(bs)) + if err != nil { + return err + } + if err = jpeg.Encode(outputFile, img, &jpeg.Options{Quality: 70}); err != nil { + return err + } + + case "png": + img, err := png.Decode(bytes.NewReader(bs)) + if err != nil { + return err + } + encoder := png.Encoder{CompressionLevel: png.BestCompression} + if err = encoder.Encode(outputFile, img); err != nil { + return err + } + + case "gif": + img, err := gif.DecodeAll(bytes.NewReader(bs)) + if err != nil { + return err + } + if err = gif.EncodeAll(outputFile, img); err != nil { + return err + } + + default: + if _, err = outputFile.Write(bs); err != nil { + return err + } + } + + return nil +} diff --git a/jwt/jwt.go b/jwt/jwt.go new file mode 100644 index 0000000..09cc38d --- /dev/null +++ b/jwt/jwt.go @@ -0,0 +1,46 @@ +package jwt + +import ( + "fmt" + + . "git.simts.cc/common/lib/types" + + "github.com/dgrijalva/jwt-go" +) + +// Encode jwt編碼 +func Encode(values Data, key string) (string, error) { + claims := jwt.MapClaims{} + for key, value := range values { + claims[key] = value + } + + tokenString, err := jwt. + NewWithClaims(jwt.SigningMethodHS256, claims). + SignedString([]byte(key)) + if err != nil { + return "", err + } + + return tokenString, nil +} + +// Decode jwt解碼 +func Decode(tokenString string, key string) (Data, error) { + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("Unexpected error: %v. ", token.Header["alg"]) + } + return []byte(key), nil + }) + if err != nil { + return nil, err + } + + claims, ok := token.Claims.(jwt.MapClaims) + if !ok || !token.Valid { + return nil, fmt.Errorf("Token error. ") + } + + return Data(claims), nil +} diff --git a/logger/logger.go b/logger/logger.go new file mode 100644 index 0000000..ec49df3 --- /dev/null +++ b/logger/logger.go @@ -0,0 +1,23 @@ +package logger + +import ( + "go.uber.org/zap" +) + +func NewLoggerProduction() (*zap.SugaredLogger, error) { + logger, err := zap.NewProduction() + if err != nil { + return nil, err + } + + return logger.Sugar(), nil +} + +func NewLoggerDevelopment() (*zap.SugaredLogger, error) { + logger, err := zap.NewDevelopment() + if err != nil { + return nil, err + } + + return logger.Sugar(), nil +} diff --git a/mail/mail.go b/mail/mail.go new file mode 100644 index 0000000..8f400b2 --- /dev/null +++ b/mail/mail.go @@ -0,0 +1,55 @@ +package mail + +import ( + "crypto/tls" + "fmt" + "net/smtp" + "strings" +) + +func Send(host string, port int, username, password, mailto, subject, content string) error { + conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", host, port), &tls.Config{InsecureSkipVerify: true, ServerName: "smtp.gmail.com"}) + if err != nil { + return err + } + + client, err := smtp.NewClient(conn, "smtp.gmail.com") + if err != nil { + return err + } + + if err = client.Auth(smtp.PlainAuth("", username, password, host)); err != nil { + return err + } + + if err = client.Mail(username); err != nil { + return err + } + + if err = client.Rcpt(mailto); err != nil { + return err + } + + w, err := client.Data() + if err != nil { + return err + } + + messages := []string{ + "Content-Type: text/html; charset=\"UTF-8\";", + fmt.Sprintf("From: %s", username), + fmt.Sprintf("To: %s", mailto), + fmt.Sprintf("Subject: %s", subject), + content, + } + + if _, err = w.Write([]byte(strings.Join(messages, "\r\n"))); err != nil { + return err + } + + if err = w.Close(); err != nil { + return err + } + + return client.Quit() +} diff --git a/types/data.go b/types/data.go new file mode 100644 index 0000000..69b43da --- /dev/null +++ b/types/data.go @@ -0,0 +1,207 @@ +package types + +import ( + "errors" +) + +var ( + ErrExistKey = errors.New("not exist.") + ErrFormat = errors.New("error format.") +) + +type Data map[string]interface{} + +func (data Data) Get(key string) (interface{}, error) { + value, exist := data[key] + if !exist { + return "", ErrExistKey + } + + return value, nil +} + +func (data Data) GetString(key string) (string, error) { + value, err := data.Get(key) + if err != nil { + return "", err + } + + switch value.(type) { + case string: + return value.(string), nil + } + + return "", ErrFormat +} + +func (data Data) GetSlice(key string) (res []interface{}, err error) { + value, err := data.Get(key) + if err != nil { + return nil, err + } + + switch value.(type) { + case []interface{}: + return value.([]interface{}), nil + default: + return nil, ErrFormat + } +} + +func (data Data) GetStringSlice(key string) (res []string, err error) { + values, err := data.GetSlice(key) + if err != nil { + return nil, err + } + + for _, v := range values { + switch v.(type) { + case string: + res = append(res, v.(string)) + default: + return nil, ErrFormat + } + } + + return res, nil +} + +func (data Data) GetIntSlice(key string) (res []int, err error) { + values, err := data.GetSlice(key) + if err != nil { + return nil, err + } + + for _, v := range values { + switch v.(type) { + case float64: + res = append(res, int(v.(float64))) + default: + return nil, ErrFormat + } + } + + return res, nil +} + +func (data Data) GetInt64(key string) (int64, error) { + value, err := data.Get(key) + if err != nil { + return 0, err + } + + switch value.(type) { + case int: + return int64(value.(int)), nil + + case int64: + return value.(int64), nil + + case float64: + return int64(value.(float64)), nil + } + + return 0, ErrFormat +} + +func (data Data) GetUint64(key string) (uint64, error) { + value, err := data.GetInt64(key) + if err != nil { + return 0, err + } + + if value < 0 { + return 0, ErrFormat + } + + return uint64(value), nil +} + +func (data Data) GetInt(key string) (int, error) { + value, err := data.GetInt64(key) + if err != nil { + return 0, err + } + + return int(value), nil +} + +func (data Data) GetUint(key string) (uint, error) { + value, err := data.GetInt64(key) + if err != nil { + return 0, err + } + + if value < 0 { + return 0, ErrFormat + } + + return uint(value), nil +} + +func (data Data) GetInt16(key string) (int16, error) { + value, err := data.GetInt64(key) + if err != nil { + return 0, err + } + + return int16(value), nil +} + +func (data Data) GetUint16(key string) (uint16, error) { + value, err := data.GetInt64(key) + if err != nil { + return 0, err + } + + if value < 0 { + return 0, ErrFormat + } + + return uint16(value), nil +} + +func (data Data) GetInt8(key string) (int8, error) { + value, err := data.GetInt64(key) + if err != nil { + return 0, err + } + + return int8(value), nil +} + +func (data Data) GetUint8(key string) (uint8, error) { + value, err := data.GetInt64(key) + if err != nil { + return 0, err + } + + if value < 0 { + return 0, ErrFormat + } + + return uint8(value), nil +} + +func (data Data) GetBool(key string) (bool, error) { + value, err := data.Get(key) + if err != nil { + return false, err + } + + switch value.(type) { + case bool: + return value.(bool), nil + } + + return false, ErrFormat +} + +func (data Data) GetTimestamp(key string) (Timestamp, error) { + value, err := data.GetInt64(key) + if err != nil { + return 0, err + } + + return Timestamp(value), nil +} diff --git a/types/page.go b/types/page.go new file mode 100644 index 0000000..f4e6d66 --- /dev/null +++ b/types/page.go @@ -0,0 +1,14 @@ +package types + +// Page 頁數 +type Page struct { + Current, Size int +} + +func (p Page) Take() int { + return p.Size +} + +func (p Page) Skip() int { + return (p.Current - 1) * p.Size +} diff --git a/types/timestamp.go b/types/timestamp.go new file mode 100644 index 0000000..c399fb7 --- /dev/null +++ b/types/timestamp.go @@ -0,0 +1,49 @@ +package types + +import ( + "time" +) + +// 格式化 +const ( + FormatDatetime = "2006-01-02 15:04:05" +) + +var Local = time.Local + +func SetLocation(name string) { + Local, _ = time.LoadLocation(name) +} + +// Timestamp 時間戳 +type Timestamp int + +func GetUnixNow() Timestamp { + return Timestamp(time.Now().Unix()) +} + +func (t Timestamp) Int64() int64 { + return int64(t) +} + +func (t Timestamp) Time() time.Time { + return time.Unix(t.Int64(), 0) +} + +// Datetime 回傳時間字串 +func (t Timestamp) Datetime() string { + return time. + Unix(t.Int64(), 0). + In(Local). + Format(FormatDatetime) +} + +// 與現在的時間差 +func (t Timestamp) NowSub() time.Duration { + return time.Since(t.Time()) +} + +// 時間差 +func (t Timestamp) Sub(t2 time.Time) time.Duration { + return t2.Sub(t.Time()) +}