This commit is contained in:
kenneth 2021-09-17 11:16:38 +08:00
parent c43ebcd5dd
commit 7833a2234c
28 changed files with 1831 additions and 0 deletions

3
.gitignore vendored
View File

@ -13,3 +13,6 @@
# Dependency directories (remove the comment below to include it) # Dependency directories (remove the comment below to include it)
# vendor/ # vendor/
.vscode
.idea

28
go.mod Normal file
View File

@ -0,0 +1,28 @@
module github.com/lyydhl-zhang/common-module
go 1.17
require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29 // indirect
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/gomodule/redigo v1.8.5 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/matoous/go-nanoid v1.5.0 // indirect
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
github.com/o1egl/paseto v1.0.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.7.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.19.1 // indirect
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 // indirect
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 // indirect
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

92
go.sum Normal file
View File

@ -0,0 +1,92 @@
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb/go.mod h1:UzH9IX1MMqOcwhoNOIjmTQeAxrFgzs50j4golQtXXxU=
github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29 h1:1DcvRPZOdbQRg5nAHt2jrc5QbV0AGuhDdfQI6gXjiFE=
github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29/go.mod h1:UzH9IX1MMqOcwhoNOIjmTQeAxrFgzs50j4golQtXXxU=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
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/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/matoous/go-nanoid v1.5.0 h1:VRorl6uCngneC4oUQqOYtO3S0H5QKFtKuKycFG3euek=
github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
github.com/o1egl/paseto v1.0.0 h1:bwpvPu2au176w4IBlhbyUv/S5VPptERIA99Oap5qUd0=
github.com/o1egl/paseto v1.0.0/go.mod h1:5HxsZPmw/3RI2pAwGo1HhOOwSdvBpcuVzO7uDkm+CLU=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 h1:3erb+vDS8lU1sxfDHF4/hhWyaXnhIaO+7RgL4fDZORA=
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 h1:7ZDGnxgHAMw7thfC5bEos0RDAccZKxioiWBhfIe+tvw=
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

17
pkg/cache/manager.go vendored Normal file
View File

@ -0,0 +1,17 @@
package cache
// Manager 管理缓存的接口定义
type Manager interface {
// Set 设置缓存
Set(key string, value interface{}) error
// SetDefault 设置缓存
SetDefault(key string, value interface{}, expire int64) error
// Get 获取缓存
Get(key string) (interface{}, error)
// GetString 获取缓存
GetString(key string) (string, error)
// Exists key是否存在
Exists(key string) bool
// Delete 删除key
Delete(key string) error
}

70
pkg/cache/memory_manager.go vendored Normal file
View File

@ -0,0 +1,70 @@
package cache
import (
"errors"
"fmt"
"time"
"github.com/patrickmn/go-cache"
)
// MemoryManager memory缓存管理
type MemoryManager struct {
cache *cache.Cache
}
// NewMemoryManager 获取一个新的内存管理对象
// defaultExpiration 缓存过期时间
// cleanupInterval 缓存清理时间
func NewMemoryManager(defaultExpiration, cleanupInterval time.Duration) Manager {
return &MemoryManager{cache: cache.New(defaultExpiration, cleanupInterval)}
}
// Set 设置缓存
// key 键
// value 值
func (manager *MemoryManager) Set(key string, value interface{}) error {
manager.cache.Set(key, value, time.Hour*24*365*100) // 10年
return nil
}
// SetDefault 设置缓存
// key 键
// value 值
// expire 过期时间 (秒)
func (manager *MemoryManager) SetDefault(key string, value interface{}, expire int64) error {
manager.cache.Set(key, value, time.Second*time.Duration(expire))
return nil
}
// Get 获取缓存
func (manager *MemoryManager) Get(key string) (interface{}, error) {
value, found := manager.cache.Get(key)
if found {
return value, nil
}
return nil, errors.New("no found data with key")
}
// GetString 获取缓存
func (manager *MemoryManager) GetString(key string) (string, error) {
value, found := manager.cache.Get(key)
if found {
return fmt.Sprintf("%v", value), nil
}
return "", errors.New("no found data with key")
}
// Exists key是否存在
func (manager *MemoryManager) Exists(key string) bool {
if _, found := manager.cache.Get(key); found {
return true
}
return false
}
// Delete 删除key
func (manager *MemoryManager) Delete(key string) error {
manager.cache.Delete(key)
return nil
}

135
pkg/cache/redis_manager.go vendored Normal file
View File

@ -0,0 +1,135 @@
package cache
import (
"fmt"
"time"
"github.com/gomodule/redigo/redis"
)
// RedisManager redis缓存管理
type RedisManager struct {
pool *redis.Pool
}
// NewRedisManager 获取一个新的redis管理对象
func NewRedisManager(address string) Manager {
pool := &redis.Pool{
// 连接方法
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", address)
if err != nil {
return nil, err
}
c.Do("SELECT", 0)
return c, nil
},
//DialContext: nil,
//TestOnBorrow: nil,
MaxIdle: 10, // 最大的空闲连接数表示即使没有redis连接时依然可以保持N个空闲的连接而不被清除随时处于待命状态。
MaxActive: 10, // 最大的激活连接数表示同时最多有N个连接
IdleTimeout: 360 * time.Second, // 最大的空闲连接等待时间,超过此时间后,空闲连接将被关闭
//Wait: false,
//MaxConnLifetime: 0,
}
return &RedisManager{pool}
}
// Set 设置缓存
// key 键
// value 值
func (manager *RedisManager) Set(key string, value interface{}) error {
conn := manager.pool.Get()
defer conn.Close()
_, err := conn.Do("Set", key, value)
if err != nil {
return fmt.Errorf("set cache error: %v", err)
}
return nil
}
// SetDefault 设置缓存
// key 键
// value 值
// expire 过期时间 (秒)
func (manager *RedisManager) SetDefault(key string, value interface{}, expire int64) error {
conn := manager.pool.Get()
defer conn.Close()
//_, err := conn.Do("set", key, value)
//if err != nil {
// return fmt.Errorf("set default cache error: %v", err)
//}
//
//// 设置过期时间
//conn.Do("expire", key, expire)
//return nil
_, err := conn.Do("set", key, value, "ex", expire)
if err != nil {
return fmt.Errorf("set default cache error: %v", err)
}
return nil
}
// Get 获取缓存
func (manager *RedisManager) Get(key string) (interface{}, error) {
conn := manager.pool.Get()
defer conn.Close()
// 检查key是否存在
exit, err := redis.Bool(conn.Do("exists", key))
if err != nil || !exit {
return nil, fmt.Errorf("key is not exists")
}
value, err := conn.Do("get", key)
if err != nil {
return nil, fmt.Errorf("get cache error: %v", err)
}
return value, nil
}
// GetString 获取缓存
func (manager *RedisManager) GetString(key string) (string, error) {
conn := manager.pool.Get()
defer conn.Close()
// 检查key是否存在
exit, err := redis.Bool(conn.Do("exists", key))
if err != nil || !exit {
return "", fmt.Errorf("key is not exists")
}
value, err := redis.String(conn.Do("get", key))
if err != nil {
return "", fmt.Errorf("get cache error: %v", err)
}
return value, nil
}
// Exists key是否存在
func (manager *RedisManager) Exists(key string) bool {
conn := manager.pool.Get()
defer conn.Close()
// 检查key是否存在
exit, err := redis.Bool(conn.Do("expire", key))
if err != nil {
return false
}
return exit
}
// Delete 删除key
func (manager *RedisManager) Delete(key string) error {
conn := manager.pool.Get()
defer conn.Close()
_, err := conn.Do("del", key)
if err != nil {
return fmt.Errorf("删除key[%s]异常:%v", key, err)
}
return nil
}

349
pkg/ip2region/ip2Region.go Normal file
View File

@ -0,0 +1,349 @@
package ip2region
import (
"errors"
"io/ioutil"
"os"
"strconv"
"strings"
)
const (
INDEX_BLOCK_LENGTH = 12
TOTAL_HEADER_LENGTH = 8192
)
var err error
var ipInfo IpInfo
type Ip2Region struct {
// db file handler
dbFileHandler *os.File
//header block info
headerSip []int64
headerPtr []int64
headerLen int64
// super block index info
firstIndexPtr int64
lastIndexPtr int64
totalBlocks int64
// for memory mode only
// the original db binary string
dbBinStr []byte
dbFile string
}
type IpInfo struct {
CityId int64
Country string
Region string
Province string
City string
ISP string
}
func (ip IpInfo)String() string {
return strconv.FormatInt(ip.CityId, 10) + "|" + ip.Country + "|" + ip.Region + "|" + ip.Province + "|" + ip.City + "|" + ip.ISP
}
func getIpInfo(cityId int64, line []byte) IpInfo {
lineSlice := strings.Split(string(line), "|")
ipInfo := IpInfo{}
length := len(lineSlice)
ipInfo.CityId = cityId
if length < 5 {
for i := 0; i <= 5 - length; i++ {
lineSlice = append(lineSlice, "")
}
}
ipInfo.Country = lineSlice[0]
ipInfo.Region = lineSlice[1]
ipInfo.Province = lineSlice[2]
ipInfo.City = lineSlice[3]
ipInfo.ISP = lineSlice[4]
return ipInfo
}
func New(path string) (*Ip2Region, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
return &Ip2Region{
dbFile:path,
dbFileHandler:file,
}, nil
}
func (this *Ip2Region) Close() {
this.dbFileHandler.Close()
}
func (this *Ip2Region) MemorySearch(ipStr string) (ipInfo IpInfo, err error) {
ipInfo = IpInfo{}
if this.totalBlocks == 0 {
this.dbBinStr, err = ioutil.ReadFile(this.dbFile)
if err != nil {
return ipInfo, err
}
this.firstIndexPtr = getLong(this.dbBinStr, 0)
this.lastIndexPtr = getLong(this.dbBinStr, 4)
this.totalBlocks = (this.lastIndexPtr - this.firstIndexPtr) /INDEX_BLOCK_LENGTH + 1
}
ip, err := ip2long(ipStr)
if err != nil {
return ipInfo, err
}
h := this.totalBlocks
var dataPtr, l int64;
for (l <= h) {
m := (l + h) >> 1
p := this.firstIndexPtr + m *INDEX_BLOCK_LENGTH
sip := getLong(this.dbBinStr, p)
if ip < sip {
h = m - 1
} else {
eip := getLong(this.dbBinStr, p + 4)
if ip > eip {
l = m + 1
} else {
dataPtr = getLong(this.dbBinStr, p + 8)
break;
}
}
}
if dataPtr == 0 {
return ipInfo, errors.New("not found")
}
dataLen := ((dataPtr >> 24) & 0xFF)
dataPtr = (dataPtr & 0x00FFFFFF);
ipInfo = getIpInfo(getLong(this.dbBinStr, dataPtr), this.dbBinStr[(dataPtr) + 4:dataPtr + dataLen])
return ipInfo, nil
}
func (this *Ip2Region)BinarySearch(ipStr string) (ipInfo IpInfo, err error) {
ipInfo = IpInfo{}
if this.totalBlocks == 0 {
this.dbFileHandler.Seek(0, 0)
superBlock := make([]byte, 8)
this.dbFileHandler.Read(superBlock)
this.firstIndexPtr = getLong(superBlock, 0)
this.lastIndexPtr = getLong(superBlock, 4)
this.totalBlocks = (this.lastIndexPtr - this.firstIndexPtr) /INDEX_BLOCK_LENGTH + 1
}
var l, dataPtr, p int64
h := this.totalBlocks
ip, err := ip2long(ipStr)
if err != nil {
return
}
for (l <= h) {
m := (l + h) >> 1
p = m * INDEX_BLOCK_LENGTH
_, err = this.dbFileHandler.Seek(this.firstIndexPtr + p, 0)
if err != nil {
return
}
buffer := make([]byte, INDEX_BLOCK_LENGTH)
_, err = this.dbFileHandler.Read(buffer)
if err != nil {
}
sip := getLong(buffer, 0)
if ip < sip {
h = m - 1
} else {
eip := getLong(buffer, 4)
if ip > eip {
l = m + 1
} else {
dataPtr = getLong(buffer, 8)
break;
}
}
}
if dataPtr == 0 {
err = errors.New("not found")
return
}
dataLen := ((dataPtr >> 24) & 0xFF)
dataPtr = (dataPtr & 0x00FFFFFF);
this.dbFileHandler.Seek(dataPtr, 0)
data := make([]byte, dataLen)
this.dbFileHandler.Read(data)
ipInfo = getIpInfo(getLong(data, 0), data[4:dataLen])
err = nil
return
}
func (this *Ip2Region) BtreeSearch(ipStr string) (ipInfo IpInfo, err error) {
ipInfo = IpInfo{}
ip, err := ip2long(ipStr)
if this.headerLen == 0 {
this.dbFileHandler.Seek(8, 0)
buffer := make([]byte, TOTAL_HEADER_LENGTH)
this.dbFileHandler.Read(buffer)
var idx int64;
for i := 0; i < TOTAL_HEADER_LENGTH; i += 8 {
startIp := getLong(buffer, int64(i))
dataPar := getLong(buffer, int64(i + 4))
if dataPar == 0 {
break
}
this.headerSip = append(this.headerSip, startIp)
this.headerPtr = append(this.headerPtr, dataPar)
idx ++;
}
this.headerLen = idx
}
var l, sptr, eptr int64
h := this.headerLen
for l <= h {
m := int64(l + h) >> 1
if m < this.headerLen {
if ip == this.headerSip[m] {
if m > 0 {
sptr = this.headerPtr[m - 1]
eptr = this.headerPtr[m]
} else {
sptr = this.headerPtr[m]
eptr = this.headerPtr[m + 1]
}
break
}
if ip < this.headerSip[m] {
if m == 0 {
sptr = this.headerPtr[m]
eptr = this.headerPtr[m + 1]
break
} else if ip > this.headerSip[m - 1] {
sptr = this.headerPtr[m - 1]
eptr = this.headerPtr[m]
break
}
h = m - 1
} else {
if m == this.headerLen - 1 {
sptr = this.headerPtr[m - 1]
eptr = this.headerPtr[m]
break
} else if ip <= this.headerSip[m + 1] {
sptr = this.headerPtr[m ]
eptr = this.headerPtr[m + 1]
break
}
l = m + 1
}
}
}
if sptr == 0 {
err = errors.New("not found")
return
}
blockLen := eptr - sptr
this.dbFileHandler.Seek(sptr, 0)
index := make([]byte, blockLen +INDEX_BLOCK_LENGTH)
this.dbFileHandler.Read(index)
var dataptr int64
h = blockLen / INDEX_BLOCK_LENGTH
l = 0
for l <= h {
m := int64(l + h) >> 1
p := m * INDEX_BLOCK_LENGTH
sip := getLong(index, p)
if ip < sip {
h = m - 1;
} else {
eip := getLong(index, p + 4)
if ip > eip {
l = m + 1
} else {
dataptr = getLong(index, p + 8)
break
}
}
}
if dataptr == 0 {
err = errors.New("not found")
return
}
dataLen := (dataptr >> 24) & 0xFF
dataPtr := dataptr & 0x00FFFFFF
this.dbFileHandler.Seek(dataPtr, 0)
data := make([]byte, dataLen)
this.dbFileHandler.Read(data)
ipInfo = getIpInfo(getLong(data, 0), data[4:])
return
}
func getLong(b []byte, offset int64) int64 {
val := (int64(b[offset ]) |
int64(b[offset + 1]) << 8 |
int64(b[offset + 2]) << 16 |
int64(b[offset + 3]) << 24)
return val
}
func ip2long(IpStr string) (int64, error) {
bits := strings.Split(IpStr, ".")
if len(bits) != 4 {
return 0, errors.New("ip format error")
}
var sum int64
for i, n := range bits {
bit, _ := strconv.ParseInt(n, 10, 64)
sum += bit << uint(24 - 8 * i)
}
return sum, nil
}

View File

@ -0,0 +1,49 @@
package ip2region
import (
"testing"
)
func BenchmarkBtreeSearch(B *testing.B) {
region, err := New("../../data/ip2region.db ")
if err != nil {
B.Error(err)
}
for i:=0;i<B.N;i++{
region.BtreeSearch("127.0.0.1")
}
}
func BenchmarkMemorySearch(B *testing.B) {
region, err := New("../../data/ip2region.db ")
if err != nil {
B.Error(err)
}
for i:=0;i<B.N;i++{
region.MemorySearch("127.0.0.1")
}
}
func BenchmarkBinarySearch(B *testing.B) {
region, err := New("../../data/ip2region.db ")
if err != nil {
B.Error(err)
}
for i:=0;i<B.N;i++{
region.BinarySearch("127.0.0.1")
}
}
func TestIp2long(t *testing.T) {
ip, err := ip2long("127.0.0.1")
if err != nil {
t.Error(err)
}
if ip != 2130706433 {
t.Error("result error")
}
t.Log(ip)
}

62
pkg/token/jwt_maker.go Normal file
View File

@ -0,0 +1,62 @@
package token
import (
"errors"
"fmt"
"time"
"github.com/dgrijalva/jwt-go"
)
const minSecretKeySize = 32
// JWTMaker JSON Web Token
type JWTMaker struct {
secretKey string
}
// NewJWTMaker 创建一个新的JWTMaker
func NewJWTMaker(secretKey string) (Maker, error) {
if len(secretKey) < minSecretKeySize {
return nil, fmt.Errorf("invalid key size: must be at least %d characters", minSecretKeySize)
}
return &JWTMaker{secretKey}, nil
}
// CreateToken 根据用户名和时间创建一个新的token
func (maker *JWTMaker) CreateToken(username string, duration time.Duration) (string, error) {
payload, err := NewPayload(username, duration)
if err != nil {
return "", err
}
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, payload)
return jwtToken.SignedString([]byte(maker.secretKey))
}
// VerifyToken checks if the token is valid or not
func (maker *JWTMaker) VerifyToken(token string) (*Payload, error) {
keyFunc := func(token *jwt.Token) (interface{}, error) {
_, ok := token.Method.(*jwt.SigningMethodHMAC)
if !ok {
return nil, ErrInvalidToken
}
return []byte(maker.secretKey), nil
}
jwtToken, err := jwt.ParseWithClaims(token, &Payload{}, keyFunc)
if err != nil {
verr, ok := err.(*jwt.ValidationError)
if ok && errors.Is(verr.Inner, ErrExpiredToken) {
return nil, ErrExpiredToken
}
return nil, ErrInvalidToken
}
payload, ok := jwtToken.Claims.(*Payload)
if !ok {
return nil, ErrInvalidToken
}
return payload, nil
}

14
pkg/token/maker.go Normal file
View File

@ -0,0 +1,14 @@
package token
import (
"time"
)
// Maker 管理token的接口定义
type Maker interface {
// CreateToken 根据用户名和时间创建一个新的token
CreateToken(username string, duration time.Duration) (string, error)
// VerifyToken 校验token是否正确
VerifyToken(token string) (*Payload, error)
}

56
pkg/token/paseto_maker.go Normal file
View File

@ -0,0 +1,56 @@
package token
import (
"fmt"
"time"
"github.com/aead/chacha20poly1305"
"github.com/o1egl/paseto"
)
// PasetoMaker is a PASETO token maker
type PasetoMaker struct {
paseto *paseto.V2
symmetricKey []byte
}
// NewPasetoMaker creates a new PasetoMaker
func NewPasetoMaker(symmetricKey string) (Maker, error) {
if len(symmetricKey) != chacha20poly1305.KeySize {
return nil, fmt.Errorf("invalid key size: must be exactly %d characters", chacha20poly1305.KeySize)
}
maker := &PasetoMaker{
paseto: paseto.NewV2(),
symmetricKey: []byte(symmetricKey),
}
return maker, nil
}
// CreateToken creates a new token for a specific username and duration
func (maker *PasetoMaker) CreateToken(username string, duration time.Duration) (string, error) {
payload, err := NewPayload(username, duration)
if err != nil {
return "", err
}
return maker.paseto.Encrypt(maker.symmetricKey, payload, nil)
}
// VerifyToken checks if the token is valid or not
func (maker *PasetoMaker) VerifyToken(token string) (*Payload, error) {
payload := &Payload{}
err := maker.paseto.Decrypt(token, maker.symmetricKey, payload, nil)
if err != nil {
return nil, ErrInvalidToken
}
err = payload.Valid()
if err != nil {
return nil, err
}
return payload, nil
}

46
pkg/token/payload.go Normal file
View File

@ -0,0 +1,46 @@
package token
import (
"errors"
"time"
"github.com/google/uuid"
)
// Different types of error returned by the VerifyToken function
var (
ErrInvalidToken = errors.New("token is invalid")
ErrExpiredToken = errors.New("token has expired")
)
// Payload contains the payload data of the token
type Payload struct {
ID uuid.UUID `json:"id"`
Username string `json:"username"`
IssuedAt time.Time `json:"issued_at"`
ExpiredAt time.Time `json:"expired_at"`
}
// NewPayload creates a new token payload with a specific username and duration
func NewPayload(username string, duration time.Duration) (*Payload, error) {
tokenID, err := uuid.NewRandom()
if err != nil {
return nil, err
}
payload := &Payload{
ID: tokenID,
Username: username,
IssuedAt: time.Now(),
ExpiredAt: time.Now().Add(duration),
}
return payload, nil
}
// Valid checks if the token payload is valid or not
func (payload *Payload) Valid() error {
if time.Now().After(payload.ExpiredAt) {
return ErrExpiredToken
}
return nil
}

33
pkg/util/cache.go Normal file
View File

@ -0,0 +1,33 @@
package util
// 简单缓存 适合小数据量
import (
"sync"
"sync/atomic"
"time"
)
var globalMap sync.Map
var cacheLen int64
func SetCache(key string, data interface{}, timeout int) {
globalMap.Store(key, data)
atomic.AddInt64(&cacheLen, 1)
time.AfterFunc(time.Second*time.Duration(timeout), func() {
atomic.AddInt64(&cacheLen, -1)
globalMap.Delete(key)
})
}
func GetCache(key string) (interface{}, bool) {
return globalMap.Load(key)
}
func DeleteCache(key string) {
globalMap.Delete(key)
}
//func LenCache() int {
// return int(cacheLen)
//}

75
pkg/util/encrypt/aes.go Normal file
View File

@ -0,0 +1,75 @@
package encrypt
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
)
var AES_KEY = "ZRxS9kLzR90GLpCFabUWMKAw"
// AESEncrypt Aes加密 key: 24个字符
func AESEncrypt(orig string, key string) (string, error) {
// 转成字节数组
origData := []byte(orig)
k := []byte(key)
// 分组秘钥
// NewCipher该函数限制了输入k的长度必须为16, 24或者32
block, err := aes.NewCipher(k)
if err != nil {
return "", err
}
// 获取秘钥块的长度
blockSize := block.BlockSize()
// 补全码
origData = PKCS7Padding(origData, blockSize)
// 加密模式
blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
// 创建数组
cryted := make([]byte, len(origData))
// 加密
blockMode.CryptBlocks(cryted, origData)
return base64.StdEncoding.EncodeToString(cryted), nil
}
// AESDecrypt Aes解密 key: 24个字符
func AESDecrypt(crypt string, key string) (string, error) {
// 转成字节数组
cryptByte, err := base64.StdEncoding.DecodeString(crypt)
if err != nil {
return "", err
}
k := []byte(key)
// 分组秘钥
block, err := aes.NewCipher(k)
if err != nil {
return "", err
}
// 获取秘钥块的长度
blockSize := block.BlockSize()
// 加密模式
blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
// 创建数组
orig := make([]byte, len(cryptByte))
// 解密
blockMode.CryptBlocks(orig, cryptByte)
// 去补全码
orig = PKCS7UnPadding(orig)
return string(orig), nil
}
// PKCS7Padding 补码
// AES加密数据块分组长度必须为128bit(byte[16])密钥长度可以是128bit(byte[16])、192bit(byte[24])、256bit(byte[32])中的任意一个。
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
paddingText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, paddingText...)
}
// PKCS7UnPadding 去码
func PKCS7UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}

View File

@ -0,0 +1,15 @@
package encrypt
import "encoding/base64"
// Base64Encrypt Base64加密
func Base64Encrypt(str string) string {
data := []byte(str)
return base64.StdEncoding.EncodeToString(data)
}
// Base64Decrypt Base64解密
func Base64Decrypt(str string) (string, error) {
decodeBytes, err := base64.StdEncoding.DecodeString(str)
return string(decodeBytes), err
}

93
pkg/util/encrypt/des.go Normal file
View File

@ -0,0 +1,93 @@
package encrypt
import (
"bytes"
"crypto/cipher"
"crypto/des"
"encoding/base64"
)
var DES_KEY = "fBEznwcv"
// DESEncrypt DES加密 key8位
func DESEncrypt(origData, key []byte) string {
//将字节秘钥转换成block快
block, _ := des.NewCipher(key)
//对明文先进行补码操作
origData = PKCS5Padding(origData, block.BlockSize())
//设置加密方式
blockMode := cipher.NewCBCEncrypter(block, key)
//创建明文长度的字节数组
crypted := make([]byte, len(origData))
//加密明文,加密后的数据放到数组中
blockMode.CryptBlocks(crypted, origData)
return base64.StdEncoding.EncodeToString(crypted)
}
// DESDecrypt DES解密 key8位
func DESDecrypt(data string, key []byte) string {
//倒叙执行一遍加密方法
//将字符串转换成字节数组
crypted, _ := base64.StdEncoding.DecodeString(data)
//将字节秘钥转换成block快
block, _ := des.NewCipher(key)
//设置解密方式
blockMode := cipher.NewCBCDecrypter(block, key)
//创建密文大小的数组变量
origData := make([]byte, len(crypted))
//解密密文到数组origData中
blockMode.CryptBlocks(origData, crypted)
//去补码
origData = PKCS5UnPadding(origData)
return string(origData)
}
// PKCS5Padding 实现明文的补码
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
//计算出需要补多少位
padding := blockSize - len(ciphertext)%blockSize
//Repeat()函数的功能是把参数一 切片复制 参数二count个,然后合成一个新的字节切片返回
// 需要补padding位的padding值
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
//把补充的内容拼接到明文后面
return append(ciphertext, padtext...)
}
// PKCS5UnPadding 去除补码
func PKCS5UnPadding(origData []byte) []byte {
length := len(origData)
// 去掉最后一个字节 unpadding 次
unpadding := int(origData[length-1])
//解密去补码时需取最后一个字节值为m则从数据尾部删除m个字节剩余数据即为加密前的原文
return origData[:(length - unpadding)]
}
// TripleDESEncrypt 3DES加密 key:24位
func TripleDESEncrypt(origData, key []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
origData = PKCS5Padding(origData, block.BlockSize())
// origData = ZeroPadding(origData, block.BlockSize())
blockMode := cipher.NewCBCEncrypter(block, key[:8])
crypted := make([]byte, len(origData))
blockMode.CryptBlocks(crypted, origData)
return crypted, nil
}
// TripleDESDecrypt 3DES解密 key:24位
func TripleDESDecrypt(crypted, key []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
blockMode := cipher.NewCBCDecrypter(block, key[:8])
origData := make([]byte, len(crypted))
// origData := crypted
blockMode.CryptBlocks(origData, crypted)
origData = PKCS5UnPadding(origData)
// origData = ZeroUnPadding(origData)
return origData, nil
}

27
pkg/util/encrypt/md5.go Normal file
View File

@ -0,0 +1,27 @@
package encrypt
import (
"crypto/md5"
"encoding/hex"
"fmt"
"io"
)
func Md5Fun01(str string) string {
return fmt.Sprintf("%x", md5.Sum([]byte(str)))
}
func Md5Fun02(str string) string {
m := md5.New()
m.Write([]byte(str))
return hex.EncodeToString(m.Sum(nil))
}
func Md5Fun03(str string) string {
m := md5.New()
_, err := io.WriteString(m, str)
if err != nil {
panic(err)
}
return fmt.Sprintf("%x", md5.Sum(nil))
}

64
pkg/util/encrypt/rsa.go Normal file
View File

@ -0,0 +1,64 @@
package encrypt
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
)
// RSAEncrypt 加密
func RSAEncrypt(origData []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, origData)
}
// RSADecrypt 解密
func RSADecrypt(ciphertext []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)
}
// 公钥和私钥可以从文件中读取
var privateKey = []byte(`
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDZsfv1qscqYdy4vY+P4e3cAtmvppXQcRvrF1cB4drkv0haU24Y
7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0DgacdwYWd/7PeCELyEipZJL07Vro7
Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NLAUeJ6PeW+DAkmJWF6QIDAQAB
AoGBAJlNxenTQj6OfCl9FMR2jlMJjtMrtQT9InQEE7m3m7bLHeC+MCJOhmNVBjaM
ZpthDORdxIZ6oCuOf6Z2+Dl35lntGFh5J7S34UP2BWzF1IyyQfySCNexGNHKT1G1
XKQtHmtc2gWWthEg+S6ciIyw2IGrrP2Rke81vYHExPrexf0hAkEA9Izb0MiYsMCB
/jemLJB0Lb3Y/B8xjGjQFFBQT7bmwBVjvZWZVpnMnXi9sWGdgUpxsCuAIROXjZ40
IRZ2C9EouwJBAOPjPvV8Sgw4vaseOqlJvSq/C/pIFx6RVznDGlc8bRg7SgTPpjHG
4G+M3mVgpCX1a/EU1mB+fhiJ2LAZ/pTtY6sCQGaW9NwIWu3DRIVGCSMm0mYh/3X9
DAcwLSJoctiODQ1Fq9rreDE5QfpJnaJdJfsIJNtX1F+L3YceeBXtW0Ynz2MCQBI8
9KP274Is5FkWkUFNKnuKUK4WKOuEXEO+LpR+vIhs7k6WQ8nGDd4/mujoJBr5mkrw
DPwqA3N5TMNDQVGv8gMCQQCaKGJgWYgvo3/milFfImbp+m7/Y3vCptarldXrYQWO
AQjxwc71ZGBFDITYvdgJM1MTqc8xQek1FXn1vfpy2c6O
-----END RSA PRIVATE KEY-----
`)
var publicKey = []byte(`
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZsfv1qscqYdy4vY+P4e3cAtmv
ppXQcRvrF1cB4drkv0haU24Y7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0Dgacd
wYWd/7PeCELyEipZJL07Vro7Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NL
AUeJ6PeW+DAkmJWF6QIDAQAB
-----END PUBLIC KEY-----
`)

33
pkg/util/encrypt/sha.go Normal file
View File

@ -0,0 +1,33 @@
package encrypt
import (
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"io"
)
// Sha1 加密
func Sha1(str string) string {
data := []byte(str)
has := sha1.Sum(data)
return fmt.Sprintf("%x", has)
}
// Sha256 加密
func Sha256(str string) string {
w := sha256.New()
io.WriteString(w, str)
bw := w.Sum(nil)
return hex.EncodeToString(bw)
}
// Sha512 加密
func Sha512(str string) string {
w := sha512.New()
io.WriteString(w, str)
bw := w.Sum(nil)
return hex.EncodeToString(bw)
}

195
pkg/util/fetcher.go Normal file
View File

@ -0,0 +1,195 @@
package util
import (
"bufio"
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"net/url"
"time"
"golang.org/x/net/html/charset"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
var rateLimiter = time.Tick(20 * time.Millisecond)
func Fetch(url string) ([]byte, error) {
<-rateLimiter
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
//fmt.Println("Error status code", resp.StatusCode)
return nil, fmt.Errorf("error status code: %d", resp.StatusCode)
}
bodyReader := bufio.NewReader(resp.Body)
e := determinEncoding(bodyReader)
utf8Reader := transform.NewReader(bodyReader, e.NewDecoder())
return io.ReadAll(utf8Reader)
}
func FetchHost(reqUrl, proxyIP string) ([]byte, error) {
<-rateLimiter
client := &http.Client{}
//是否使用代理IP
if proxyIP != "" {
proxy, err := url.Parse(proxyIP)
if err != nil {
log.Fatalf("1:%v\n", err)
}
netTransport := &http.Transport{
Proxy: http.ProxyURL(proxy),
MaxIdleConnsPerHost: 10,
ResponseHeaderTimeout: time.Second * time.Duration(5),
}
client = &http.Client{
Timeout: time.Second * 10,
Transport: netTransport,
}
}
req, err := http.NewRequest("GET", reqUrl, nil)
if err != nil {
log.Fatalln(err)
}
req.Header.Set("Host", "www.stats.gov.cn")
//req.Header.Set("Cookie", "_trs_uv=kma79tru_6_1yw6; SF_cookie_1=37059734; wzws_cid=ed0db2d09a630ccd20292459e4bfc8091d460d5990f415e3d928761ee970d90f7ba6254f9c4b8e9b79ad456094d4a1381305d0b065ff9cc539ee1775ec262f946af61ddbed371e2a6dae2bc2041a30fc")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.54")
req.Header.Set("Upgrade-Insecure-Requests", "1")
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
//fmt.Println("Error status code", resp.StatusCode)
return nil, fmt.Errorf("error status code: %d", resp.StatusCode)
}
bodyReader := bufio.NewReader(resp.Body)
e := determinEncoding(bodyReader)
utf8Reader := transform.NewReader(bodyReader, e.NewDecoder())
return io.ReadAll(utf8Reader)
}
// HttpPost 模拟请求方法
func HttpPost(postUrl string, headers map[string]string, jsonMap map[string]interface{}, proxyIP string) ([]byte, error) {
client := &http.Client{}
//转换成postBody
bytesData, err := json.Marshal(jsonMap)
if err != nil {
fmt.Println(err.Error())
return nil, err
}
postBody := bytes.NewReader(bytesData)
//是否使用代理IP
if proxyIP != "" {
//proxy := func(_ *http.Request) (*url.URL, error) {
// return url.Parse(proxyIP)
//}
transport := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
proxyUrl, err := url.Parse(proxyIP)
if err == nil { // 使用传入代理
transport.Proxy = http.ProxyURL(proxyUrl)
}
//&http.Transport{Proxy: proxy}
client = &http.Client{Transport: transport}
} else {
client = &http.Client{}
}
// get请求
req, err := http.NewRequest("GET", postUrl, postBody)
if err != nil {
log.Fatalln(err)
return nil, err
}
for k, v := range headers {
req.Header.Add(k, v)
}
resp, err := client.Do(req)
if err != nil {
log.Fatalln(err)
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
//fmt.Println("Error status code", resp.StatusCode)
return nil, fmt.Errorf("error status code: %d", resp.StatusCode)
}
bodyReader := bufio.NewReader(resp.Body)
e := determinEncoding(bodyReader)
utf8Reader := transform.NewReader(bodyReader, e.NewDecoder())
return io.ReadAll(utf8Reader)
}
func determinEncoding(r *bufio.Reader) encoding.Encoding {
bytes, err := r.Peek(1024)
if err != nil && err != io.EOF {
log.Printf("Fetcher error: %v", err)
return unicode.UTF8
}
e, _, _ := charset.DetermineEncoding(bytes, "")
return e
}
// PostRequest 请求
func PostRequest(url, parameter string) ([]byte, error) {
client := &http.Client{}
byteParameter := bytes.NewBuffer([]byte(parameter))
request, _ := http.NewRequest("POST", url, byteParameter)
request.Header.Set("Content-type", "application/json")
response, _ := client.Do(request)
if response.StatusCode != 200 {
return nil, errors.New("网络请求失败")
}
all, err := io.ReadAll(response.Body)
if err != nil {
return nil, errors.New("读取网络内容失败")
}
return all, nil
}
// PostRequestString 请求
func PostRequestString(url, parameter string) ([]byte, error) {
client := &http.Client{}
byteParameter := bytes.NewBuffer([]byte(parameter))
request, _ := http.NewRequest("POST", url, byteParameter)
request.Header.Set("Content-type", "application/x-www-form-urlencoded")
response, _ := client.Do(request)
if response.StatusCode != 200 {
return nil, errors.New("网络请求失败")
}
all, err := io.ReadAll(response.Body)
if err != nil {
return nil, errors.New("读取网络内容失败")
}
return all, nil
}

34
pkg/util/file.go Normal file
View File

@ -0,0 +1,34 @@
package util
import "os"
// 判断所给路径文件/文件夹是否存在
func Exists(path string) bool {
_, err := os.Stat(path) //os.Stat获取文件信息
if err != nil {
if os.IsExist(err) {
return true
}
return false
}
return true
}
// 如果不存在则新建文件夹
func IsNotExistMkDir(src string) error {
if notExist := Exists(src); !notExist {
if err := MkDir(src); err != nil {
return err
}
}
return nil
}
// 新建文件夹
func MkDir(src string) error {
err := os.MkdirAll(src, os.ModePerm)
if err != nil {
return err
}
return nil
}

140
pkg/util/location.go Normal file
View File

@ -0,0 +1,140 @@
package util
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)
type LocationResponse struct {
ShowApiResCode int `json:"showapi_res_code"`
ShowApiResError string `json:"showapi_res_error"`
ShowApiResBody LocationBodyResponse `json:"showapi_res_body"`
}
type LocationBodyResponse struct {
// 0为成功其他失败。失败时不扣点数
RetCode int `json:"ret_code"`
// 1移动 2电信 3联通
Type int `json:"type"`
// 号段 1890871
Num int `json:"num"`
// 网络 服务商
Name string `json:"name"`
// 此地区身份证号开头几位 530000
ProvCode string `json:"prov_code"`
// 邮编 650000
PostCode string `json:"post_code"`
// 省份 云南
Prov string `json:"prov"`
// 城市 昆明
City string `json:"city"`
// 区号 0871
AreaCode string `json:"area_code"`
}
func GetLocationWithPhone(phone string) (LocationResponse, error) {
url := "http://showphone.market.alicloudapi.com/6-1?num=" + phone
appCode := "ee6e2d724f3a42f4881c36ec90b9ef4c"
client := &http.Client{Timeout: time.Second * 5}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatalln(err)
}
req.Header.Set("Authorization", "APPCODE "+appCode)
var out LocationResponse
get, err := client.Do(req)
if err != nil {
return out, fmt.Errorf("接口请求失败:%v", err)
}
defer get.Body.Close()
res, err := ioutil.ReadAll(get.Body)
if err != nil {
return out, fmt.Errorf("body读取失败%v", err)
}
log.Printf("1: %#v", res)
err = json.Unmarshal(res, &out)
if err != nil {
return out, fmt.Errorf("json反序列化失败%v", err)
}
return out, nil
}
type RegionResponse struct {
ShowApiResCode int `json:"showapi_res_code"`
ShowApiResError string `json:"showapi_res_error"`
ShowApiResBody RegionBodyResponse `json:"showapi_res_body"`
}
type RegionBodyResponse struct {
// 0为成功其他失败。失败时不扣点数
RetCode int `json:"ret_code"`
// 县
county string `json:"county"`
// 网络 服务商
ISP string `json:"isp"`
// IP
IP string `json:"ip"`
// 国家
Country string `json:"country"`
// 省份 云南
Region string `json:"region"`
// 城市 昆明
City string `json:"city"`
}
func GetRegionWithIP(ip string) (RegionResponse, error) {
url := "http://saip.market.alicloudapi.com/ip?ip=" + ip
appCode := "ee6e2d724f3a42f4881c36ec90b9ef4c"
client := &http.Client{Timeout: time.Second * 5}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatalln(err)
}
req.Header.Set("Authorization", "APPCODE "+appCode)
var out RegionResponse
get, err := client.Do(req)
if err != nil {
return out, fmt.Errorf("接口请求失败:%v", err)
}
defer get.Body.Close()
res, err := ioutil.ReadAll(get.Body)
if err != nil {
return out, fmt.Errorf("body读取失败%v", err)
}
log.Printf("2: %#v", res)
err = json.Unmarshal(res, &out)
if err != nil {
return out, fmt.Errorf("json反序列化失败%v", err)
}
return out, nil
}

33
pkg/util/log.go Normal file
View File

@ -0,0 +1,33 @@
package util
import (
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func NewLogger() *zap.SugaredLogger {
writeSyncer := getLogWriter()
encoder := getEncoder()
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
logger := zap.New(core, zap.AddCaller())
return logger.Sugar()
}
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
}
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./log/run.log", // 日志文件的位置
MaxSize: 10, // 在进行切割之前日志文件的最大大小以MB为单位
MaxBackups: 100, // 保留旧文件的最大个数
MaxAge: 365, // 保留旧文件的最大天数
Compress: false, // 是否压缩/归档旧文件
}
return zapcore.AddSync(lumberJackLogger)
}

17
pkg/util/md5.go Normal file
View File

@ -0,0 +1,17 @@
package util
import (
"crypto/md5"
"encoding/hex"
"fmt"
)
func Md5(str string) string {
return fmt.Sprintf("%x", md5.Sum([]byte(str)))
}
func EncodeMD5(value string) string {
m := md5.New()
m.Write([]byte(value))
return hex.EncodeToString(m.Sum(nil))
}

20
pkg/util/pager.go Normal file
View File

@ -0,0 +1,20 @@
package util
// InitPageNo 初始化pageNo
func InitPageNo(pageNo int) int {
if pageNo < 1 {
pageNo = 1
}
return pageNo
}
// InitPageSize 初始化pageSize
func InitPageSize(pageSize int) int {
if pageSize < 1 {
pageSize = 10
}
if pageSize > 20 {
pageSize = 20
}
return pageSize
}

21
pkg/util/password.go Normal file
View File

@ -0,0 +1,21 @@
package util
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
// HashPassword returns the bcrypt hash of the password
func HashPassword(password string) (string, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", fmt.Errorf("failed to hash password: %w", err)
}
return string(hashedPassword), nil
}
// CheckPassword checks if the provided password is correct or not
func CheckPassword(password string, hashedPassword string) error {
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
}

51
pkg/util/random.go Normal file
View File

@ -0,0 +1,51 @@
package util
import (
"fmt"
"math/rand"
"strings"
"time"
gonanoid "github.com/matoous/go-nanoid"
)
const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
func init() {
rand.Seed(time.Now().UnixNano())
}
func RandomString(n int) string {
var sb strings.Builder
k := len(alphabet)
for i := 0; i < n; i++ {
c := alphabet[rand.Intn(k)]
sb.WriteByte(c)
}
return sb.String()
}
func RandomInt(n int) string {
var letters = []byte("0123456789")
result := make([]byte, n)
for i := range result {
result[i] = letters[rand.Intn(len(letters))]
}
return string(result)
}
func RandomLevel() string {
levels := []string{"DEBUG", "INFO", "ERROR", "WARNING", "FAIL"}
n := len(levels)
return levels[rand.Intn(n)]
}
func GenerateFilename() (string, error) {
filename, err := gonanoid.Nanoid()
if err != nil {
return "", fmt.Errorf("could not generate avatar filename: %v", err)
}
return filename, nil
}

59
pkg/util/string.go Normal file
View File

@ -0,0 +1,59 @@
package util
import (
"regexp"
"strconv"
"strings"
)
func RemoveHTML(str string) string {
if len(str) > 0 {
//删除脚本
reg := regexp.MustCompile(`([\r\n])[\s]+`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`<script[^>]*?>.*?</script>`)
str = reg.ReplaceAllString(str, "")
//删除HTML
reg = regexp.MustCompile(`<(.[^>]*)>`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`([\r\n])[\s]+`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`-->`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`<!--.*`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`&(quot|#34);`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`&(amp|#38);`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`&(lt|#60);`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`&(gt|#62);`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`&(nbsp|#160);`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`&(iexcl|#161);`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`&(cent|#162);`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`&(pound|#163);`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`&(copy|#169);`)
str = reg.ReplaceAllString(str, "")
reg = regexp.MustCompile(`&#(\d+);`)
str = reg.ReplaceAllString(str, "")
str = strings.ReplaceAll(str, "<", "")
str = strings.ReplaceAll(str, ">", "")
str = strings.ReplaceAll(str, "\n", "")
str = strings.ReplaceAll(str, " ", "")
str = strings.ReplaceAll(str, " ", "")
return str
}
return ""
}
func FloatToString(val float64) string {
return strconv.FormatFloat(val, 'f', 1, 64)
}