Compare commits

...

31 Commits

Author SHA1 Message Date
Stanly 26602e7552 Optimize
Stanly 826dee2e35 Add jwt function is token expired
Stanly ba1e5f096f Fix value type
Stanly b1fc83ab93 Add slice (int, uint, int8, uint8, int16, uint16, int64, uint64, float64)
Stanly 0b835b0a5c Remove logger
stanly aff9de10bc Update jwt version
stanly 0994d07bfa Add goland .gitignore
Stanly 19e5e81945 Add timestamp millisecond
Stanly 8caad09446 fix Date & Datetime
Stanly e832a78b72 Add generate random key
Stanly 2edb855dd1 fix value type
Stanly 29b17ab180 Add openssl aes
Stanly 0528a80f7b Add jwt EncodeRS256
Stanly 9a3d7a87fd Update lint
Stanly b9fe8c9ef2 Update crypto
1. Add Md5 encrypt
2. Add NewKeyRSA
3. Update aes with key, iv
Stanly 066f6829bd Add sms every8d, mitake, smsget
Stanly 9db4b3652f Add Slice push, pop, unshift, shift, json
Stanly c015dceee0 Fix GetInt64 type error
Stanly dfc1397b9b Add get page
Stanly 827bf55c44 Add timestamp after & afternow
Stanly f05613cd19 Add get float64
Stanly ec508ad7da Add get data
Stanly 3c4e62ce60 Fix data get timestamp format
Stanly c0ab765d37 Add get data slice
Stanly 603578a494 Update GetUnix parse in location
Stanly 7ca8b5f462 Add timestamp func date
Stanly 658a912c58 Add GetUnix, MustGetUnix
Stanly a0f6d80b86 Add MustGetInt16
Stanly d003330393 Add func comment
Stanly 56b2b22f66 Add data json
Stanly 2a78c5e845 Add must get func

1
.gitignore vendored

@ -0,0 +1 @@
/.idea

@ -1,125 +0,0 @@
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"

@ -4,11 +4,13 @@ import (
"github.com/mojocn/base64Captcha" "github.com/mojocn/base64Captcha"
) )
// Generate 產生驗證圖示
func Generate() (string, string, error) { func Generate() (string, string, error) {
driver := base64Captcha.NewDriverDigit(80, 160, 4, 0.1, 4) driver := base64Captcha.NewDriverDigit(80, 160, 4, 0.1, 4)
return base64Captcha.NewCaptcha(driver, base64Captcha.DefaultMemStore).Generate() return base64Captcha.NewCaptcha(driver, base64Captcha.DefaultMemStore).Generate()
} }
// Verify 驗證驗證碼
func Verify(id, code string) bool { func Verify(id, code string) bool {
return base64Captcha.DefaultMemStore.Verify(id, code, true) return base64Captcha.DefaultMemStore.Verify(id, code, true)
} }

@ -0,0 +1,106 @@
package crypto
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"fmt"
"github.com/Luzifer/go-openssl/v3"
)
func addPKCS7Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padText...)
}
func removePKCS7Padding(data []byte) ([]byte, error) {
if len(data) == 0 {
return nil, fmt.Errorf("data cannot be empty")
}
padding := int(data[len(data)-1])
if padding > len(data) {
return nil, fmt.Errorf("invalid padding size")
}
return data[:len(data)-padding], nil
}
// EncryptAES 加密函式
func EncryptAES(plaintext, key, iv []byte) ([]byte, error) {
// 驗證密鑰長度是否符合 AES 的要求
if len(key) != aes.BlockSize && len(key) != 16 && len(key) != 24 && len(key) != 32 {
return nil, fmt.Errorf("invalid key size: %d, expected 16, 24, or 32", len(key))
}
// 創建 AES 密碼器
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
}
// 確保輸入的資料長度符合 block size並進行填充
blockSize := block.BlockSize()
plaintext = addPKCS7Padding(plaintext, blockSize)
// 創建 CBC 模式加密器
blockMode := cipher.NewCBCEncrypter(block, iv)
// 用來保存加密結果的緩衝區
enc := make([]byte, len(plaintext))
blockMode.CryptBlocks(enc, plaintext)
return enc, nil
}
// DecryptAES 使用 AES CBC 模式進行解密
func DecryptAES(ciphertext, key, iv []byte) ([]byte, error) {
// 驗證密鑰長度是否符合 AES 的要求
if len(key) != aes.BlockSize && len(key) != 16 && len(key) != 24 && len(key) != 32 {
return nil, fmt.Errorf("invalid key size: %d, expected 16, 24, or 32", len(key))
}
// 創建 AES 密碼器
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
}
// 確保輸入的資料長度符合 block size並進行解密
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, iv[:blockSize])
// 用來保存解密結果的緩衝區
origData := make([]byte, len(ciphertext))
blockMode.CryptBlocks(origData, ciphertext)
// 去除 PKCS7 填充
return removePKCS7Padding(origData)
}
// EncryptAESWithOpenSSL 使用Openssl 加密AES
func EncryptAESWithOpenSSL(value, key string) ([]byte, error) {
o := openssl.New()
enc, err := o.EncryptBytes(key, []byte(value), openssl.DigestMD5Sum)
if err != nil {
return nil, fmt.Errorf("failed to encrypt with OpenSSL: %w", err)
}
return enc, nil
}
// DecryptAESWithOpenSSL 使用Openssl 解密AES
func DecryptAESWithOpenSSL(value, key string) ([]byte, error) {
o := openssl.New()
dec, err := o.DecryptBytes(key, []byte(value), openssl.DigestMD5Sum)
if err != nil {
return nil, fmt.Errorf("failed to decrypt with OpenSSL: %w", err)
}
return dec, nil
}

@ -1,95 +1,23 @@
package crypto package crypto
import ( import (
"crypto/rand" "crypto/md5"
"crypto/rsa"
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
"crypto/x509"
"encoding/pem"
"errors"
"fmt" "fmt"
"github.com/Luzifer/go-openssl/v3"
"golang.org/x/crypto/bcrypt"
) )
// MD5 回傳md5加密
func MD5(v string) string {
return fmt.Sprintf("%x", md5.Sum([]byte(v)))
}
// SHA1 回傳sha1加密 // SHA1 回傳sha1加密
func SHA1(v string) string { func SHA1(v string) string {
h := sha1.New() return fmt.Sprintf("%x", sha1.Sum([]byte(v)))
h.Write([]byte(v))
bs := h.Sum(nil)
return fmt.Sprintf("%x", bs)
} }
// SHA256 回傳sha256加密 // SHA256 回傳sha256加密
func SHA256(v string) string { func SHA256(v string) string {
h := sha256.New() return fmt.Sprintf("%x", sha256.Sum256([]byte(v)))
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 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
}
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 EncryptRSA(value, publicKey []byte) ([]byte, error) {
block, _ := pem.Decode(publicKey)
if block == nil {
return nil, errors.New("public key error")
}
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
pub := pubInterface.(*rsa.PublicKey)
return rsa.EncryptPKCS1v15(rand.Reader, pub, value)
}
func DecryptRSA(ciphertext, privateKey []byte) ([]byte, error) {
block, _ := pem.Decode(privateKey)
if block == nil {
return nil, errors.New("private key error!")
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
} }

@ -0,0 +1,29 @@
package crypto
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
// EncryptPassword 加密密碼
func EncryptPassword(pwd string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
if err != nil {
return "", fmt.Errorf("failed to encrypt password: %w", err)
}
return string(hash), nil
}
// CheckPassword 檢查密碼
func CheckPassword(pwd, hash string) error {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(pwd))
if err != nil {
// 返回帶有更多上下文信息的錯誤
return fmt.Errorf("password does not match: %w", err)
}
// 如果密碼正確,返回 nil
return nil
}

@ -0,0 +1,86 @@
package crypto
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
)
func NewKeyRSA(bitSize int) (pubPEM []byte, keyPEM []byte, err error) {
// 生成 RSA 密鑰對
key, err := rsa.GenerateKey(rand.Reader, bitSize)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate RSA key: %w", err)
}
// 提取公鑰部分並轉換為 PEM 格式
pubBytes, err := x509.MarshalPKIXPublicKey(key.Public())
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal public key: %w", err)
}
pubPEM = pem.EncodeToMemory(
&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubBytes,
},
)
// 編碼私鑰為 PKCS#1 ASN.1 PEM 格式
keyPEM = pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
},
)
return pubPEM, keyPEM, nil
}
// EncryptRSA rsa加密
func EncryptRSA(value, publicKey []byte) ([]byte, error) {
block, _ := pem.Decode(publicKey)
if block == nil {
return nil, errors.New("failed to decode public key PEM")
}
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse public key: %w", err)
}
pub, ok := pubInterface.(*rsa.PublicKey)
if !ok {
return nil, errors.New("invalid public key type")
}
// 使用公鑰進行加密
enc, err := rsa.EncryptPKCS1v15(rand.Reader, pub, value)
if err != nil {
return nil, fmt.Errorf("failed to encrypt with RSA: %w", err)
}
return enc, nil
}
// DecryptRSA rsa解密
func DecryptRSA(ciphertext, privateKey []byte) ([]byte, error) {
block, _ := pem.Decode(privateKey)
if block == nil {
return nil, errors.New("failed to decode private key PEM")
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse private key: %w", err)
}
dec, err := rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
if err != nil {
return nil, fmt.Errorf("failed to decrypt with RSA: %w", err)
}
return dec, nil
}

@ -2,33 +2,37 @@ package generate
import ( import (
"crypto/rand" "crypto/rand"
"math/big" "fmt"
) )
// GetRandomString 取得隨機字串 // GetRandomString 取得隨機字串
func GetRandomString(n int) (string, error) { func GetRandomString(n int) (string, error) {
const alphaNum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" const alphaNum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
return getRandomFromCharset(n, alphaNum)
}
// GetRandomKey 取得隨機金鑰
func GetRandomKey(n int) (string, error) {
const alphaNum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!@#$%^&*()-_=+"
return getRandomFromCharset(n, alphaNum)
}
// getRandomFromCharset 通用隨機字串生成器
func getRandomFromCharset(n int, charset string) (string, error) {
// 預先計算字符集的長度
charsLen := len(charset)
buffer := make([]byte, n) buffer := make([]byte, n)
max := big.NewInt(int64(len(alphaNum)))
for i := 0; i < n; i++ { // 使用 crypto/rand 生成隨機數據
index, err := randomInt(max) _, err := rand.Read(buffer)
if err != nil { if err != nil {
return "", err return "", fmt.Errorf("failed to generate random data: %w", err)
} }
buffer[i] = alphaNum[index] // 從字元集選擇字符
for i := 0; i < n; i++ {
buffer[i] = charset[int(buffer[i])%charsLen]
} }
return string(buffer), nil 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
}

@ -1,11 +1,18 @@
module git.simts.cc/common/lib module git.simts.cc/common/lib
go 1.14 go 1.19
require ( require (
github.com/Luzifer/go-openssl/v3 v3.1.0 github.com/Luzifer/go-openssl/v3 v3.1.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/golang-jwt/jwt/v5 v5.0.0-rc.2
github.com/mojocn/base64Captcha v1.3.1 github.com/mojocn/base64Captcha v1.3.1
go.uber.org/zap v1.15.0 go.uber.org/zap v1.15.0
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/crypto v0.8.0
)
require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
go.uber.org/atomic v1.6.0 // indirect
go.uber.org/multierr v1.5.0 // indirect
golang.org/x/image v0.7.0 // indirect
) )

@ -5,16 +5,14 @@ github.com/Luzifer/go-openssl/v3 v3.1.0/go.mod h1:liy3FXuuS8hfDlYh1T+l78AwQ/NjZf
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/golang-jwt/jwt/v5 v5.0.0-rc.2 h1:hXPcSazn8wKOfSb9y2m1bdgUMlDxVDarxh3lJVbC6JE=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/golang-jwt/jwt/v5 v5.0.0-rc.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= 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/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/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/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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 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/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 h1:2Wbkt8Oc8qjmNJ5GyOfSo4tgVQPsbKMftqASnq8GlT0=
github.com/mojocn/base64Captcha v1.3.1/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY= github.com/mojocn/base64Captcha v1.3.1/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY=
@ -27,6 +25,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 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 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
@ -36,30 +35,55 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= 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-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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/image v0.0.0-20190501045829-6d32002ffd75 h1:TbGuee8sSq15Iguxu4deQ7+Bqq/d2rsQejGcEtADAMQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= 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/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/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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-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/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 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-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-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/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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 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/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/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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

@ -6,34 +6,49 @@ import (
"image/jpeg" "image/jpeg"
"image/png" "image/png"
"io" "io"
"io/ioutil"
"mime/multipart" "mime/multipart"
"os" "os"
) )
// CompressImage 壓縮圖片至指定位置
func CompressImage(input interface{}, output string) (err error) { func CompressImage(input interface{}, output string) (err error) {
var file io.Reader var file io.Reader
switch input.(type) { var fileToClose io.Closer
switch v := input.(type) {
case string: case string:
if file, err = os.Open(input.(string)); err != nil { f, err := os.Open(v)
if err != nil {
return err return err
} }
defer file.(*os.File).Close()
defer os.Remove(input.(string))
file = f
fileToClose = f
defer func() {
if fileToClose != nil {
_ = fileToClose.Close()
}
_ = os.Remove(v)
}()
case *multipart.FileHeader: case *multipart.FileHeader:
fileHeader := input.(*multipart.FileHeader) f, err := v.Open()
file, err = fileHeader.Open()
if err != nil { if err != nil {
return err return err
} }
defer file.(multipart.File).Close() file = f
fileToClose = f
defer func() {
if fileToClose != nil {
_ = fileToClose.Close()
}
}()
} }
bs, err := ioutil.ReadAll(file) var buf bytes.Buffer
if err != nil { if _, err = io.Copy(&buf, file); err != nil {
return err return err
} }
bs := buf.Bytes()
var ext string var ext string
if _, err = jpeg.Decode(bytes.NewReader(bs)); err == nil { if _, err = jpeg.Decode(bytes.NewReader(bs)); err == nil {
@ -48,7 +63,11 @@ func CompressImage(input interface{}, output string) (err error) {
if err != nil { if err != nil {
return err return err
} }
defer outputFile.Close() defer func() {
if outputFile != nil {
_ = outputFile.Close()
}
}()
switch ext { switch ext {
case "jpg": case "jpg":
@ -56,34 +75,22 @@ func CompressImage(input interface{}, output string) (err error) {
if err != nil { if err != nil {
return err return err
} }
if err = jpeg.Encode(outputFile, img, &jpeg.Options{Quality: 70}); err != nil { return jpeg.Encode(outputFile, img, &jpeg.Options{Quality: 70})
return err
}
case "png": case "png":
img, err := png.Decode(bytes.NewReader(bs)) img, err := png.Decode(bytes.NewReader(bs))
if err != nil { if err != nil {
return err return err
} }
encoder := png.Encoder{CompressionLevel: png.BestCompression} encoder := png.Encoder{CompressionLevel: png.BestCompression}
if err = encoder.Encode(outputFile, img); err != nil { return encoder.Encode(outputFile, img)
return err
}
case "gif": case "gif":
img, err := gif.DecodeAll(bytes.NewReader(bs)) img, err := gif.DecodeAll(bytes.NewReader(bs))
if err != nil { if err != nil {
return err return err
} }
if err = gif.EncodeAll(outputFile, img); err != nil { return gif.EncodeAll(outputFile, img)
return err
}
default: default:
if _, err = outputFile.Write(bs); err != nil { _, err = outputFile.Write(bs)
return err return err
} }
} }
return nil
}

@ -1,46 +1,71 @@
package jwt package jwt
import ( import (
"crypto/rsa"
"errors"
"fmt" "fmt"
. "git.simts.cc/common/lib/types" "git.simts.cc/common/lib/types"
"github.com/golang-jwt/jwt/v5"
"github.com/dgrijalva/jwt-go"
) )
// Encode jwt編碼 // Encode jwt編碼
func Encode(values Data, key string) (string, error) { func Encode(values types.Data, key string) (string, error) {
claims := jwt.MapClaims{} claims := jwt.MapClaims{}
for key, value := range values { for k, v := range values {
claims[key] = value claims[k] = v
} }
tokenString, err := jwt. tokenString, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).
NewWithClaims(jwt.SigningMethodHS256, claims).
SignedString([]byte(key)) SignedString([]byte(key))
if err != nil { if err != nil {
return "", err return "", fmt.Errorf("failed to sign JWT with HS256: %w", err)
}
return tokenString, nil
}
// EncodeRS256 jwt編碼
func EncodeRS256(values types.Data, key *rsa.PrivateKey) (string, error) {
claims := jwt.MapClaims{}
for k, v := range values {
claims[k] = v
}
tokenString, err := jwt.NewWithClaims(jwt.SigningMethodRS256, claims).
SignedString(key)
if err != nil {
return "", fmt.Errorf("failed to sign JWT with RS256: %w", err)
} }
return tokenString, nil return tokenString, nil
} }
// Decode jwt解碼 // Decode jwt解碼
func Decode(tokenString string, key string) (Data, error) { func Decode(tokenString string, key string) (types.Data, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected error: %v. ", token.Header["alg"]) return nil, fmt.Errorf("unexpected signing method: %w", token.Header["alg"])
} }
return []byte(key), nil return []byte(key), nil
}) })
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to parse JWT: %w", err)
}
if token == nil || !token.Valid {
return nil, fmt.Errorf("invalid token or claims")
} }
claims, ok := token.Claims.(jwt.MapClaims) claims, ok := token.Claims.(jwt.MapClaims)
if !ok || !token.Valid { if !ok {
return nil, fmt.Errorf("Token error. ") return nil, fmt.Errorf("failed to parse token claims")
}
return types.Data(claims), nil
} }
return Data(claims), nil // IsExpired 檢查 token 是否過期
func IsExpired(err error) bool {
return errors.Is(err, jwt.ErrTokenExpired)
} }

@ -1,23 +0,0 @@
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
}

@ -7,6 +7,7 @@ import (
"strings" "strings"
) )
// Send 寄送信件
func Send(host string, port int, username, password, mailto, subject, content string) error { 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"}) conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", host, port), &tls.Config{InsecureSkipVerify: true, ServerName: "smtp.gmail.com"})
if err != nil { if err != nil {

@ -0,0 +1,10 @@
package sms
import (
"fmt"
)
var (
ErrResponse = fmt.Errorf("Resposne error.")
ErrNotEnoughCredit = fmt.Errorf("Not enough credit.")
)

@ -0,0 +1,59 @@
package sms
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
type Every8D struct {
Username string
Password string
}
func NewSmsEvery8D(username, password string) *Every8D {
return &Every8D{
Username: username,
Password: password,
}
}
func (every8d *Every8D) Send(mobile, msg string) (int, error) {
qs := url.Values{}
qs.Add("UID", every8d.Username)
qs.Add("PWD", every8d.Password)
qs.Add("DEST", mobile)
qs.Add("MSG", url.QueryEscape(msg))
client := http.Client{Timeout: 2 * time.Second}
resp, err := client.Get(fmt.Sprintf("https://oms.every8d.com/API21/HTTP/sendSMS.ashx?%s", qs.Encode()))
if err != nil {
return 0, err
}
bs, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 0, err
}
defer resp.Body.Close()
data := strings.Split(string(bs), ",")
if len(data) != 5 {
return 0, ErrResponse
}
credit, err := strconv.ParseFloat(data[0], 64)
if err != nil {
return 0, err
}
if credit < 0 {
return 0, ErrNotEnoughCredit
}
return int(credit), nil
}

@ -0,0 +1,62 @@
package sms
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
type Mitake struct {
Username string
Password string
}
func NewSmsMitake(username, password string) *Mitake {
return &Mitake{
Username: username,
Password: password,
}
}
func (mitake *Mitake) Send(mobile, msg string) (int, error) {
qs := url.Values{}
qs.Add("CharsetURL", "UTF8")
qs.Add("username", mitake.Username)
qs.Add("password", mitake.Password)
qs.Add("dstaddr", mobile)
qs.Add("smbody", url.QueryEscape(msg))
client := http.Client{Timeout: 2 * time.Second}
resp, err := client.Get(fmt.Sprintf("http://smsb2c.mitake.com.tw/b2c/mtk/SmSend?%s", qs.Encode()))
if err != nil {
return 0, err
}
bs, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 0, err
}
defer resp.Body.Close()
if len(strings.Split(string(bs), "\n")) != 6 {
return 0, ErrResponse
}
qs, err = url.ParseQuery(strings.ReplaceAll(string(bs), "\n", "&"))
if err != nil {
return 0, err
}
credit, err := strconv.ParseInt(strings.ReplaceAll(qs.Get("AccountPoint"), "\r", ""), 10, 64)
if err != nil {
return 0, err
} else if credit < 0 {
return 0, ErrNotEnoughCredit
}
return int(credit), nil
}

@ -0,0 +1,64 @@
package sms
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
)
type SmsGet struct {
Username string
Password string
}
func NewSmsGet(username, password string) *SmsGet {
return &SmsGet{
Username: username,
Password: password,
}
}
func (smsget *SmsGet) Send(mobile, msg string) (int, error) {
qs := url.Values{}
qs.Add("method", "1")
qs.Add("username", smsget.Username)
qs.Add("password", smsget.Password)
qs.Add("sms_msg", url.QueryEscape(msg))
resp, err := http.Get(fmt.Sprintf("https://sms-get2.com/api_send.php?%s", qs.Encode()))
if err != nil {
return 0, err
}
defer resp.Body.Close()
bs, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 0, err
}
var res struct {
Status bool `json:"stats"`
Code int `json:"error_code,string"`
Msg string `json:"error_msg"`
}
if err := json.Unmarshal(bs, &res); err != nil {
return 0, ErrResponse
}
if !res.Status || res.Code != 0 {
if res.Code == 9 {
return 0, ErrNotEnoughCredit
}
return 0, ErrResponse
}
credit, err := strconv.Atoi(strings.Split(res.Msg, "|")[2])
if err != nil {
return 0, err
}
return credit, nil
}

@ -1,16 +1,20 @@
package types package types
import ( import (
"encoding/json"
"errors" "errors"
) )
// 錯誤訊息
var ( var (
ErrExistKey = errors.New("not exist.") ErrExistKey = errors.New("not exist")
ErrFormat = errors.New("error format.") ErrFormat = errors.New("error format")
) )
// Data 資料
type Data map[string]interface{} type Data map[string]interface{}
// Get 取得資料
func (data Data) Get(key string) (interface{}, error) { func (data Data) Get(key string) (interface{}, error) {
value, exist := data[key] value, exist := data[key]
if !exist { if !exist {
@ -20,62 +24,201 @@ func (data Data) Get(key string) (interface{}, error) {
return value, nil return value, nil
} }
// GetSlice 取得切片
func (data Data) GetSlice(key string) (res Slice, err error) {
value, err := data.Get(key)
if err != nil {
return nil, err
}
switch v := value.(type) {
case []interface{}:
return v, nil
default:
return nil, ErrFormat
}
}
// MustGetSlice 強制取得切片
func (data Data) MustGetSlice(key string) Slice {
values, _ := data.GetSlice(key)
return values
}
// GetString 取得字串
func (data Data) GetString(key string) (string, error) { func (data Data) GetString(key string) (string, error) {
value, err := data.Get(key) value, err := data.Get(key)
if err != nil { if err != nil {
return "", err return "", err
} }
switch value.(type) { switch value := value.(type) {
case []byte:
return string(value), nil
case string: case string:
return value.(string), nil return value, nil
default:
return "", ErrFormat
}
} }
return "", ErrFormat // MustGetString 強制取得字串
func (data Data) MustGetString(key string) string {
value, _ := data.GetString(key)
return value
} }
func (data Data) GetSlice(key string) (res []interface{}, err error) { // GetStringSlice 取得字串切片
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 := v.(type) {
case string:
res = append(res, v)
default:
return nil, ErrFormat
}
}
return res, nil
}
// MustGetStringSlice 強制取得字串切片
func (data Data) MustGetStringSlice(key string) []string {
values, _ := data.GetStringSlice(key)
return values
}
// GetBytes 取得字串
func (data Data) GetBytes(key string) ([]byte, error) {
value, err := data.Get(key) value, err := data.Get(key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
switch value.(type) { switch value := value.(type) {
case []interface{}: case []byte:
return value.([]interface{}), nil return value, nil
case string:
return []byte(value), nil
default: default:
return nil, ErrFormat return nil, ErrFormat
} }
} }
func (data Data) GetStringSlice(key string) (res []string, err error) { // MustGetBytes 強制取得字串
func (data Data) MustGetBytes(key string) []byte {
value, _ := data.GetBytes(key)
return value
}
// GetData 取得資料
func (data Data) GetData(key string) (Data, error) {
value, err := data.Get(key)
if err != nil {
return nil, err
}
switch v := value.(type) {
case map[string]interface{}:
return v, nil
default:
return nil, ErrFormat
}
}
// MustGetData 強制取得資料
func (data Data) MustGetData(key string) Data {
value, _ := data.GetData(key)
return value
}
// GetDataSlice 取得資料切片
func (data Data) GetDataSlice(key string) ([]Data, error) {
values, err := data.GetSlice(key) values, err := data.GetSlice(key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, v := range values { var list []Data
switch v.(type) { for _, value := range values {
case string: switch v := value.(type) {
res = append(res, v.(string)) case map[string]interface{}:
list = append(list, v)
default: default:
return nil, ErrFormat return nil, ErrFormat
} }
} }
return res, nil return list, nil
} }
func (data Data) GetIntSlice(key string) (res []int, err error) { // MustGetDataSlice 強制取得資料切片
func (data Data) MustGetDataSlice(key string) []Data {
values, _ := data.GetDataSlice(key)
return values
}
// GetFloat64 取得64位元浮點數
func (data Data) GetFloat64(key string) (float64, error) {
value, err := data.Get(key)
if err != nil {
return 0, err
}
switch value := value.(type) {
case float32:
return float64(value), nil
case float64:
return value, nil
default:
return 0, ErrFormat
}
}
// MustGetFloat64 強制取得64位元浮點數
func (data Data) MustGetFloat64(key string) float64 {
value, _ := data.GetFloat64(key)
return value
}
// GetFloat64Slice 取得64位元浮點數切片
func (data Data) GetFloat64Slice(key string) (res []float64, err error) {
values, err := data.GetSlice(key) values, err := data.GetSlice(key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, v := range values { for _, v := range values {
switch v.(type) { switch v := v.(type) {
case int:
res = append(res, float64(v))
case uint:
res = append(res, float64(v))
case int8:
res = append(res, float64(v))
case uint8:
res = append(res, float64(v))
case int16:
res = append(res, float64(v))
case uint16:
res = append(res, float64(v))
case int32:
res = append(res, float64(v))
case uint32:
res = append(res, float64(v))
case int64:
res = append(res, float64(v))
case uint64:
res = append(res, float64(v))
case float32:
res = append(res, float64(v))
case float64: case float64:
res = append(res, int(v.(float64))) res = append(res, v)
default: default:
return nil, ErrFormat return nil, ErrFormat
} }
@ -84,26 +227,99 @@ func (data Data) GetIntSlice(key string) (res []int, err error) {
return res, nil return res, nil
} }
// MustGetFloat64Slice 強制取得64位元浮點數切片
func (data Data) MustGetFloat64Slice(key string) []float64 {
values, _ := data.GetFloat64Slice(key)
return values
}
// GetInt64 取得64位元整數
func (data Data) GetInt64(key string) (int64, error) { func (data Data) GetInt64(key string) (int64, error) {
value, err := data.Get(key) value, err := data.Get(key)
if err != nil { if err != nil {
return 0, err return 0, err
} }
switch value.(type) { switch value := value.(type) {
case int: case int:
return int64(value.(int)), nil return int64(value), nil
case uint:
return int64(value), nil
case int8:
return int64(value), nil
case uint8:
return int64(value), nil
case int16:
return int64(value), nil
case uint16:
return int64(value), nil
case int32:
return int64(value), nil
case uint32:
return int64(value), nil
case int64: case int64:
return value.(int64), nil return value, nil
case float64:
return int64(value), nil
default:
return 0, ErrFormat
}
}
// MustGetInt64 強制取得64位元整數
func (data Data) MustGetInt64(key string) int64 {
value, _ := data.GetInt64(key)
return value
}
// GetInt64Slice 取得64位元整數切片
func (data Data) GetInt64Slice(key string) (res []int64, err error) {
values, err := data.GetSlice(key)
if err != nil {
return nil, err
}
for _, v := range values {
switch v := v.(type) {
case int:
res = append(res, int64(v))
case uint:
res = append(res, int64(v))
case int8:
res = append(res, int64(v))
case uint8:
res = append(res, int64(v))
case int16:
res = append(res, int64(v))
case uint16:
res = append(res, int64(v))
case int32:
res = append(res, int64(v))
case uint32:
res = append(res, int64(v))
case int64:
res = append(res, v)
case uint64:
res = append(res, int64(v))
case float32:
res = append(res, int64(v))
case float64: case float64:
return int64(value.(float64)), nil res = append(res, int64(v))
default:
return nil, ErrFormat
}
} }
return 0, ErrFormat return res, nil
}
// MustGetInt64Slice 強制取得64位元整數切片
func (data Data) MustGetInt64Slice(key string) []int64 {
values, _ := data.GetInt64Slice(key)
return values
} }
// GetUint64 取得64位元正整數
func (data Data) GetUint64(key string) (uint64, error) { func (data Data) GetUint64(key string) (uint64, error) {
value, err := data.GetInt64(key) value, err := data.GetInt64(key)
if err != nil { if err != nil {
@ -117,6 +333,60 @@ func (data Data) GetUint64(key string) (uint64, error) {
return uint64(value), nil return uint64(value), nil
} }
// MustGetUint64 強制取得64位元正整數
func (data Data) MustGetUint64(key string) uint64 {
value, _ := data.GetUint64(key)
return value
}
// GetUint64Slice 取得64位元正整數切片
func (data Data) GetUint64Slice(key string) (res []uint64, err error) {
values, err := data.GetSlice(key)
if err != nil {
return nil, err
}
for _, v := range values {
switch v := v.(type) {
case int:
res = append(res, uint64(v))
case uint:
res = append(res, uint64(v))
case int8:
res = append(res, uint64(v))
case uint8:
res = append(res, uint64(v))
case int16:
res = append(res, uint64(v))
case uint16:
res = append(res, uint64(v))
case int32:
res = append(res, uint64(v))
case uint32:
res = append(res, uint64(v))
case int64:
res = append(res, uint64(v))
case uint64:
res = append(res, v)
case float32:
res = append(res, uint64(v))
case float64:
res = append(res, uint64(v))
default:
return nil, ErrFormat
}
}
return res, nil
}
// MustGetUint64Slice 強制取得64位元正整數切片
func (data Data) MustGetUint64Slice(key string) []uint64 {
values, _ := data.GetUint64Slice(key)
return values
}
// GetInt 取得整數
func (data Data) GetInt(key string) (int, error) { func (data Data) GetInt(key string) (int, error) {
value, err := data.GetInt64(key) value, err := data.GetInt64(key)
if err != nil { if err != nil {
@ -126,6 +396,60 @@ func (data Data) GetInt(key string) (int, error) {
return int(value), nil return int(value), nil
} }
// MustGetInt 強制取得整數
func (data Data) MustGetInt(key string) int {
value, _ := data.GetInt(key)
return value
}
// GetIntSlice 取得整數切片
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 := v.(type) {
case int:
res = append(res, v)
case uint:
res = append(res, int(v))
case int8:
res = append(res, int(v))
case uint8:
res = append(res, int(v))
case int16:
res = append(res, int(v))
case uint16:
res = append(res, int(v))
case int32:
res = append(res, int(v))
case uint32:
res = append(res, int(v))
case int64:
res = append(res, int(v))
case uint64:
res = append(res, int(v))
case float32:
res = append(res, int(v))
case float64:
res = append(res, int(v))
default:
return nil, ErrFormat
}
}
return res, nil
}
// MustGetIntSlice 強制取得整數切片
func (data Data) MustGetIntSlice(key string) []int {
values, _ := data.GetIntSlice(key)
return values
}
// GetUint 取得正整數
func (data Data) GetUint(key string) (uint, error) { func (data Data) GetUint(key string) (uint, error) {
value, err := data.GetInt64(key) value, err := data.GetInt64(key)
if err != nil { if err != nil {
@ -139,6 +463,60 @@ func (data Data) GetUint(key string) (uint, error) {
return uint(value), nil return uint(value), nil
} }
// MustGetUint 強制取得正整數
func (data Data) MustGetUint(key string) uint {
value, _ := data.GetUint(key)
return value
}
// GetUintSlice 取得正整數切片
func (data Data) GetUintSlice(key string) (res []uint, err error) {
values, err := data.GetSlice(key)
if err != nil {
return nil, err
}
for _, v := range values {
switch v := v.(type) {
case int:
res = append(res, uint(v))
case uint:
res = append(res, v)
case int8:
res = append(res, uint(v))
case uint8:
res = append(res, uint(v))
case int16:
res = append(res, uint(v))
case uint16:
res = append(res, uint(v))
case int32:
res = append(res, uint(v))
case uint32:
res = append(res, uint(v))
case int64:
res = append(res, uint(v))
case uint64:
res = append(res, uint(v))
case float32:
res = append(res, uint(v))
case float64:
res = append(res, uint(v))
default:
return nil, ErrFormat
}
}
return res, nil
}
// MustGetUintSlice 強制取得正整數切片
func (data Data) MustGetUintSlice(key string) []uint {
values, _ := data.GetUintSlice(key)
return values
}
// GetInt16 取得16位元整數
func (data Data) GetInt16(key string) (int16, error) { func (data Data) GetInt16(key string) (int16, error) {
value, err := data.GetInt64(key) value, err := data.GetInt64(key)
if err != nil { if err != nil {
@ -148,6 +526,60 @@ func (data Data) GetInt16(key string) (int16, error) {
return int16(value), nil return int16(value), nil
} }
// MustGetInt16 強制取得16位元整數
func (data Data) MustGetInt16(key string) uint16 {
value, _ := data.GetUint16(key)
return value
}
// GetInt16Slice 取得16位元整數切片
func (data Data) GetInt16Slice(key string) (res []int16, err error) {
values, err := data.GetSlice(key)
if err != nil {
return nil, err
}
for _, v := range values {
switch v := v.(type) {
case int:
res = append(res, int16(v))
case uint:
res = append(res, int16(v))
case int8:
res = append(res, int16(v))
case uint8:
res = append(res, int16(v))
case int16:
res = append(res, v)
case uint16:
res = append(res, int16(v))
case int32:
res = append(res, int16(v))
case uint32:
res = append(res, int16(v))
case int64:
res = append(res, int16(v))
case uint64:
res = append(res, int16(v))
case float32:
res = append(res, int16(v))
case float64:
res = append(res, int16(v))
default:
return nil, ErrFormat
}
}
return res, nil
}
// MustGetInt16Slice 強制取得16位元整數切片
func (data Data) MustGetInt16Slice(key string) []int16 {
values, _ := data.GetInt16Slice(key)
return values
}
// GetUint16 取得16位元正整數
func (data Data) GetUint16(key string) (uint16, error) { func (data Data) GetUint16(key string) (uint16, error) {
value, err := data.GetInt64(key) value, err := data.GetInt64(key)
if err != nil { if err != nil {
@ -161,6 +593,60 @@ func (data Data) GetUint16(key string) (uint16, error) {
return uint16(value), nil return uint16(value), nil
} }
// MustGetUint16 強制取得16位元正整數
func (data Data) MustGetUint16(key string) uint16 {
value, _ := data.GetUint16(key)
return value
}
// GetUint16Slice 取得16位元正整數切片
func (data Data) GetUint16Slice(key string) (res []uint16, err error) {
values, err := data.GetSlice(key)
if err != nil {
return nil, err
}
for _, v := range values {
switch v := v.(type) {
case int:
res = append(res, uint16(v))
case uint:
res = append(res, uint16(v))
case int8:
res = append(res, uint16(v))
case uint8:
res = append(res, uint16(v))
case int16:
res = append(res, uint16(v))
case uint16:
res = append(res, v)
case int32:
res = append(res, uint16(v))
case uint32:
res = append(res, uint16(v))
case int64:
res = append(res, uint16(v))
case uint64:
res = append(res, uint16(v))
case float32:
res = append(res, uint16(v))
case float64:
res = append(res, uint16(v))
default:
return nil, ErrFormat
}
}
return res, nil
}
// MustGetUint16Slice 強制取得16位元正整數切片
func (data Data) MustGetUint16Slice(key string) []uint16 {
values, _ := data.GetUint16Slice(key)
return values
}
// GetInt8 取得8位元整數
func (data Data) GetInt8(key string) (int8, error) { func (data Data) GetInt8(key string) (int8, error) {
value, err := data.GetInt64(key) value, err := data.GetInt64(key)
if err != nil { if err != nil {
@ -170,6 +656,60 @@ func (data Data) GetInt8(key string) (int8, error) {
return int8(value), nil return int8(value), nil
} }
// MustGetInt8 強制取得8位元整數
func (data Data) MustGetInt8(key string) int8 {
value, _ := data.GetInt8(key)
return value
}
// GetInt8Slice 取得8位元整數切片
func (data Data) GetInt8Slice(key string) (res []int8, err error) {
values, err := data.GetSlice(key)
if err != nil {
return nil, err
}
for _, v := range values {
switch v := v.(type) {
case int:
res = append(res, int8(v))
case uint:
res = append(res, int8(v))
case int8:
res = append(res, v)
case uint8:
res = append(res, int8(v))
case int16:
res = append(res, int8(v))
case uint16:
res = append(res, int8(v))
case int32:
res = append(res, int8(v))
case uint32:
res = append(res, int8(v))
case int64:
res = append(res, int8(v))
case uint64:
res = append(res, int8(v))
case float32:
res = append(res, int8(v))
case float64:
res = append(res, int8(v))
default:
return nil, ErrFormat
}
}
return res, nil
}
// MustGetInt8Slice 強制取得8位元整數切片
func (data Data) MustGetInt8Slice(key string) []int8 {
values, _ := data.GetInt8Slice(key)
return values
}
// GetUint8 取得8位元正整數
func (data Data) GetUint8(key string) (uint8, error) { func (data Data) GetUint8(key string) (uint8, error) {
value, err := data.GetInt64(key) value, err := data.GetInt64(key)
if err != nil { if err != nil {
@ -183,25 +723,152 @@ func (data Data) GetUint8(key string) (uint8, error) {
return uint8(value), nil return uint8(value), nil
} }
// MustGetUint8 強制取得8位元正整數
func (data Data) MustGetUint8(key string) uint8 {
value, _ := data.GetUint8(key)
return value
}
// GetUint8Slice 取得8位元正整數切片
func (data Data) GetUint8Slice(key string) (res []uint8, err error) {
values, err := data.GetSlice(key)
if err != nil {
return nil, err
}
for _, v := range values {
switch v := v.(type) {
case int:
res = append(res, uint8(v))
case uint:
res = append(res, uint8(v))
case int8:
res = append(res, uint8(v))
case uint8:
res = append(res, v)
case int16:
res = append(res, uint8(v))
case uint16:
res = append(res, uint8(v))
case int32:
res = append(res, uint8(v))
case uint32:
res = append(res, uint8(v))
case int64:
res = append(res, uint8(v))
case uint64:
res = append(res, uint8(v))
case float32:
res = append(res, uint8(v))
case float64:
res = append(res, uint8(v))
default:
return nil, ErrFormat
}
}
return res, nil
}
// MustGetUint8Slice 強制取得8位元正整數切片
func (data Data) MustGetUint8Slice(key string) []uint8 {
values, _ := data.GetUint8Slice(key)
return values
}
// GetBool 取得布林值
func (data Data) GetBool(key string) (bool, error) { func (data Data) GetBool(key string) (bool, error) {
value, err := data.Get(key) value, err := data.Get(key)
if err != nil { if err != nil {
return false, err return false, err
} }
switch value.(type) { switch value := value.(type) {
case uint8:
return value > 0, nil
case int64:
return value > 0, nil
case float64:
return value > 0, nil
case bool: case bool:
return value.(bool), nil return value, nil
} }
return false, ErrFormat return false, ErrFormat
} }
// MustGetBool 強制取得布林值
func (data Data) MustGetBool(key string) bool {
value, _ := data.GetBool(key)
return value
}
// GetTimestamp 取得時間戳記
func (data Data) GetTimestamp(key string) (Timestamp, error) { func (data Data) GetTimestamp(key string) (Timestamp, error) {
value, err := data.GetInt64(key) value, err := data.Get(key)
if err != nil { if err != nil {
return 0, err return 0, err
} }
switch value := value.(type) {
case int:
return Timestamp(value), nil
case uint:
return Timestamp(value), nil
case int64:
return Timestamp(value), nil return Timestamp(value), nil
case uint64:
return Timestamp(value), nil
case float64:
return Timestamp(value), nil
case Timestamp:
return value, nil
}
return 0, ErrFormat
}
// MustGetTimestamp 強制取得時間戳記
func (data Data) MustGetTimestamp(key string) Timestamp {
value, _ := data.GetTimestamp(key)
return value
}
// JSON 取得json
func (data Data) JSON() ([]byte, error) {
bs, err := json.Marshal(&data)
if err != nil {
return nil, err
}
return bs, nil
}
// MustJSON 強制取得json
func (data Data) MustJSON() []byte {
bs, _ := data.JSON()
return bs
}
// GetPage 取得頁數
func (data Data) GetPage(key string) (*Page, error) {
value, err := data.Get(key)
if err != nil {
return nil, err
}
switch value := value.(type) {
case Page:
return &value, nil
}
return nil, ErrFormat
}
// MustGetPage 強制取得頁數
func (data Data) MustGetPage(key string) *Page {
value, _ := data.GetPage(key)
return value
} }

@ -5,10 +5,12 @@ type Page struct {
Current, Size int Current, Size int
} }
// Take 取得數量
func (p Page) Take() int { func (p Page) Take() int {
return p.Size return p.Size
} }
// Skip 忽略數量
func (p Page) Skip() int { func (p Page) Skip() int {
return (p.Current - 1) * p.Size return (p.Current - 1) * p.Size
} }

@ -0,0 +1,55 @@
package types
import (
"encoding/json"
)
type Slice []interface{}
// Push 後堆
func (slice *Slice) Push(value interface{}) {
*slice = append(*slice, value)
}
// Pop 後取
func (slice *Slice) Pop() (value interface{}) {
length := len(*slice)
if length > 0 {
value = (*slice)[length-1]
*slice = (*slice)[:length-1]
}
return value
}
// Unshift 前堆
func (slice *Slice) Unshift(value interface{}) {
*slice = append(Slice{value}, *slice...)
}
// Shift 前取
func (slice *Slice) Shift() (value interface{}) {
length := len(*slice)
if length > 0 {
value = (*slice)[0]
*slice = (*slice)[1:length]
}
return value
}
// JSON 取得json
func (slice *Slice) JSON() ([]byte, error) {
bs, err := json.Marshal(slice)
if err != nil {
return nil, err
}
return bs, nil
}
// MustJSON 強制取得json
func (slice *Slice) MustJSON() []byte {
bs, _ := slice.JSON()
return bs
}

@ -6,44 +6,116 @@ import (
// 格式化 // 格式化
const ( const (
FormatDate = "2006-01-02"
FormatDatetime = "2006-01-02 15:04:05" FormatDatetime = "2006-01-02 15:04:05"
) )
var Local = time.Local // Location 本地時區
var Location = time.Local
// SetLocation 設定時區
func SetLocation(name string) { func SetLocation(name string) {
Local, _ = time.LoadLocation(name) Location, _ = time.LoadLocation(name)
} }
// Timestamp 時間戳 // Timestamp 時間戳
type Timestamp int64 type Timestamp int64
// GetUnixNow 取得現在時間戳記
func GetUnixNow() Timestamp { func GetUnixNow() Timestamp {
return Timestamp(time.Now().Unix()) return Timestamp(time.Now().Unix())
} }
// GetUnixMilliNow 取得現在時間戳記
func GetUnixMilliNow() Timestamp {
return Timestamp(time.Now().UnixMilli())
}
// GetUnix 嘗試取得時間戳記
func GetUnix(format, datetime string) (Timestamp, error) {
t, err := time.ParseInLocation(format, datetime, Location)
return Timestamp(t.Unix()), err
}
// MustGetUnix 強制取得時間戳記
func MustGetUnix(format, datetime string) Timestamp {
t, _ := GetUnix(format, datetime)
return t
}
// Int64 轉為64位元整數
func (t Timestamp) Int64() int64 { func (t Timestamp) Int64() int64 {
return int64(t) return int64(t)
} }
// Time 轉為time類型
func (t Timestamp) Time() time.Time { func (t Timestamp) Time() time.Time {
return time.Unix(t.Int64(), 0) return time.Unix(t.Int64(), 0)
} }
// TimeMilli 轉為time類型
func (t Timestamp) TimeMilli() time.Time {
return time.UnixMilli(t.Int64())
}
// Date 回傳日期字串
func (t Timestamp) Date() string {
if t == 0 {
return ""
}
return time.
Unix(t.Int64(), 0).
In(Location).
Format(FormatDate)
}
// Datetime 回傳時間字串 // Datetime 回傳時間字串
func (t Timestamp) Datetime() string { func (t Timestamp) Datetime() string {
if t == 0 {
return ""
}
return time. return time.
Unix(t.Int64(), 0). Unix(t.Int64(), 0).
In(Local). In(Location).
Format(FormatDatetime) Format(FormatDatetime)
} }
// 與現在的時間差 // Since 與現在的時間差
func (t Timestamp) NowSub() time.Duration { func (t Timestamp) Since() time.Duration {
return time.Since(t.Time()) return time.Since(t.Time())
} }
// 時間差 // SinceMilli 與現在的時間差
func (t Timestamp) SinceMilli() time.Duration {
return time.Since(t.TimeMilli())
}
// Sub 時間差
func (t Timestamp) Sub(t2 time.Time) time.Duration { func (t Timestamp) Sub(t2 time.Time) time.Duration {
return t2.Sub(t.Time()) return t2.Sub(t.Time())
} }
// SubMilli 時間差
func (t Timestamp) SubMilli(t2 time.Time) time.Duration {
return t2.Sub(t.TimeMilli())
}
// After 是否在指定時間後
func (t Timestamp) After(t2 time.Time) bool {
return t.Time().After(t2)
}
// AfterMilli 是否在指定時間後
func (t Timestamp) AfterMilli(t2 time.Time) bool {
return t.TimeMilli().After(t2)
}
// AfterNow 是否在目前時間後
func (t Timestamp) AfterNow() bool {
return t.After(time.Now())
}
// AfterMilliNow 是否在目前時間後
func (t Timestamp) AfterMilliNow() bool {
return t.AfterMilli(time.Now())
}

Loading…
Cancel
Save