This commit is contained in:
2022-04-07 15:20:21 +08:00
parent 19ea9c5a16
commit 811536bea4
35 changed files with 466 additions and 543 deletions

View File

@@ -1,4 +1,4 @@
package util
package cache
// 简单缓存 适合小数据量
@@ -27,7 +27,3 @@ func GetCache(key string) (interface{}, bool) {
func DeleteCache(key string) {
globalMap.Delete(key)
}
//func LenCache() int {
// return int(cacheLen)
//}

17
pkg/cache/manager.go vendored
View File

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

View File

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

View File

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

7
pkg/conver/conver.go Normal file
View File

@@ -0,0 +1,7 @@
package conver
import "strconv"
func FloatToString(val float64) string {
return strconv.FormatFloat(val, 'f', 1, 64)
}

16
pkg/database/mssql.go Normal file
View File

@@ -0,0 +1,16 @@
package database
import (
mssql "github.com/denisenkom/go-mssqldb"
)
// MSIsUniqueViolation 唯一值是否冲突 ture: 是, false: 否
func MSIsUniqueViolation(err error) bool {
msErr, ok := err.(mssql.Error)
return ok && msErr.Number == 2627
}
//func MsIsForeignKeyViolation(err error) bool {
// msErr, ok := err.(mssql.Error)
// return ok && msErr.Number == 0
//}

15
pkg/database/postgres.go Normal file
View File

@@ -0,0 +1,15 @@
package database
import (
"github.com/lib/pq"
)
func PGIsUniqueViolation(err error) bool {
pqErr, ok := err.(*pq.Error)
return ok && pqErr.Code == "23505"
}
func PGIsForeignKeyViolation(err error) bool {
pqErr, ok := err.(*pq.Error)
return ok && pqErr.Code == "23503"
}

44
pkg/database/query.go Normal file
View File

@@ -0,0 +1,44 @@
package database
import (
"bytes"
"fmt"
"strings"
"sync"
"text/template"
)
var queriesCache sync.Map
func BuildQuery(text string, data map[string]interface{}) (string, []interface{}, error) {
var t *template.Template
v, ok := queriesCache.Load(text)
if !ok {
var err error
t, err = template.New("query").Parse(text)
if err != nil {
return "", nil, fmt.Errorf("could not parse sql query template: %w", err)
}
queriesCache.Store(text, t)
} else {
t = v.(*template.Template)
}
var wr bytes.Buffer
if err := t.Execute(&wr, data); err != nil {
return "", nil, fmt.Errorf("could not apply sql query data: %w", err)
}
query := wr.String()
var args []interface{}
for key, val := range data {
if !strings.Contains(query, "@"+key) {
continue
}
args = append(args, val)
query = strings.Replace(query, "@"+key, fmt.Sprintf("$%d", len(args)), -1)
}
return query, args, nil
}

12
pkg/dt/time.go Normal file
View File

@@ -0,0 +1,12 @@
package dt
import "time"
func GetTime(t string) (time.Time, error) {
now := time.Now()
d, err := time.ParseDuration(t)
if err != nil {
return now, err
}
return now.Add(d), nil
}

View File

@@ -70,6 +70,6 @@ func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
// PKCS7UnPadding 去码
func PKCS7UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
unPadding := int(origData[length-1])
return origData[:(length - unPadding)]
}

12
pkg/encrypt/md5.go Normal file
View File

@@ -0,0 +1,12 @@
package encrypt
import (
"crypto/md5"
"encoding/hex"
)
func MD5(value string) string {
m := md5.New()
m.Write([]byte(value))
return hex.EncodeToString(m.Sum(nil))
}

72
pkg/encrypt/password.go Normal file
View File

@@ -0,0 +1,72 @@
package encrypt
import (
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/scrypt"
)
// ******************** scrypt ********************
// ScryptHashPassword scrypt 加密
// password 原始密码
func ScryptHashPassword(password string) (string, error) {
// example for making salt - https://play.golang.org/p/_Aw6WeWC42I
salt := make([]byte, 32)
_, err := rand.Read(salt)
if err != nil {
return "", err
}
// using recommended cost parameters from - https://godoc.org/golang.org/x/crypto/scrypt
shash, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
if err != nil {
return "", err
}
// return hex-encoded string with salt appended to password
hashedPW := fmt.Sprintf("%s.%s", hex.EncodeToString(shash), hex.EncodeToString(salt))
return hashedPW, nil
}
// ScryptComparePassword 判断密码是否正确
// storedPassword 加密密码
// suppliedPassword 原始密码
func ScryptComparePassword(storedPassword string, suppliedPassword string) (bool, error) {
pwsalt := strings.Split(storedPassword, ".")
// check supplied password salted with hash
salt, err := hex.DecodeString(pwsalt[1])
if err != nil {
return false, fmt.Errorf("unable to verify user password")
}
shash, err := scrypt.Key([]byte(suppliedPassword), salt, 32768, 8, 1, 32)
return hex.EncodeToString(shash) == pwsalt[0], nil
}
// ******************** bcrypt ********************
// BcryptHashPassword bcrypt 加密
// password 原始密码
func BcryptHashPassword(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
}
// BcryptComparePassword 判断密码是否正确
// hashedPassword 加密密码
// password 原始密码
func BcryptComparePassword(hashedPassword string, password string) error {
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
}

12
pkg/env/env.go vendored Normal file
View File

@@ -0,0 +1,12 @@
package env
import "os"
func GetEnvWithDefault(name, def string) string {
env := os.Getenv(name)
if len(env) != 0 {
return env
}
return def
}

80
pkg/fetcher/fetcher.go Normal file
View File

@@ -0,0 +1,80 @@
package fetcher
import (
"bufio"
"bytes"
"errors"
"io"
"io/ioutil"
"log"
"net/http"
"time"
"golang.org/x/net/html/charset"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/unicode"
)
// Get get 请求
func Get(url string) ([]byte, error) {
client := &http.Client{}
req, _ := http.NewRequest("GET", url, nil)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
res, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return res, nil
}
// PostJson application/json post 请求
func PostJson(url, parameter string, timeout int) ([]byte, error) {
client := &http.Client{Timeout: time.Second * time.Duration(timeout)}
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
}
// PostString application/x-www-form-urlencoded 请求
func PostString(url, parameter string, timeout int) ([]byte, error) {
client := &http.Client{Timeout: time.Second * time.Duration(timeout)}
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 := ioutil.ReadAll(response.Body)
if err != nil {
return nil, errors.New("读取网络内容失败")
}
return all, nil
}
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
}

View File

@@ -1,4 +1,4 @@
package util
package ft
import "os"
@@ -31,4 +31,4 @@ func MkDir(src string) error {
return err
}
return nil
}
}

View File

@@ -13,19 +13,17 @@ const (
TOTAL_HEADER_LENGTH = 8192
)
var err error
var ipInfo IpInfo
var IpRegion *Ip2Region
type Ip2Region struct {
// db file handler
dbFileHandler *os.File
//header block info
headerSip []int64
headerPtr []int64
headerLen int64
headerSip []int64
headerPtr []int64
headerLen int64
// super block index info
firstIndexPtr int64
@@ -35,20 +33,20 @@ type Ip2Region struct {
// for memory mode only
// the original db binary string
dbBinStr []byte
dbFile string
dbBinStr []byte
dbFile string
}
type IpInfo struct {
CityId int64
Country string
Region string
Province string
City string
ISP string
CityId int64 `json:"city_id"`
Country string `json:"country"`
Region string `json:"region"`
Province string `json:"province"`
City string `json:"city"`
ISP string `json:"isp"`
}
func (ip IpInfo)String() string {
func (ip IpInfo) String() string {
return strconv.FormatInt(ip.CityId, 10) + "|" + ip.Country + "|" + ip.Region + "|" + ip.Province + "|" + ip.City + "|" + ip.ISP
}
@@ -59,7 +57,7 @@ func getIpInfo(cityId int64, line []byte) IpInfo {
length := len(lineSlice)
ipInfo.CityId = cityId
if length < 5 {
for i := 0; i <= 5 - length; i++ {
for i := 0; i <= 5-length; i++ {
lineSlice = append(lineSlice, "")
}
}
@@ -72,37 +70,39 @@ func getIpInfo(cityId int64, line []byte) IpInfo {
return ipInfo
}
func New(path string) (*Ip2Region, error) {
func New(path string) error {
file, err := os.Open(path)
if err != nil {
return nil, err
return err
}
return &Ip2Region{
dbFile:path,
dbFileHandler:file,
}, nil
IpRegion = &Ip2Region{
dbFile: path,
dbFileHandler: file,
}
return nil
}
func (this *Ip2Region) Close() {
this.dbFileHandler.Close()
func (region *Ip2Region) Close() error {
return region.dbFileHandler.Close()
}
func (this *Ip2Region) MemorySearch(ipStr string) (ipInfo IpInfo, err error) {
func (region *Ip2Region) MemorySearch(ipStr string) (ipInfo IpInfo, err error) {
ipInfo = IpInfo{}
if this.totalBlocks == 0 {
this.dbBinStr, err = ioutil.ReadFile(this.dbFile)
if region.totalBlocks == 0 {
region.dbBinStr, err = ioutil.ReadFile(region.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
region.firstIndexPtr = getLong(region.dbBinStr, 0)
region.lastIndexPtr = getLong(region.dbBinStr, 4)
region.totalBlocks = (region.lastIndexPtr-region.firstIndexPtr)/INDEX_BLOCK_LENGTH + 1
}
ip, err := ip2long(ipStr)
@@ -110,22 +110,22 @@ func (this *Ip2Region) MemorySearch(ipStr string) (ipInfo IpInfo, err error) {
return ipInfo, err
}
h := this.totalBlocks
var dataPtr, l int64;
for (l <= h) {
h := region.totalBlocks
var dataPtr, l int64
for l <= h {
m := (l + h) >> 1
p := this.firstIndexPtr + m *INDEX_BLOCK_LENGTH
sip := getLong(this.dbBinStr, p)
p := region.firstIndexPtr + m*INDEX_BLOCK_LENGTH
sip := getLong(region.dbBinStr, p)
if ip < sip {
h = m - 1
} else {
eip := getLong(this.dbBinStr, p + 4)
eip := getLong(region.dbBinStr, p+4)
if ip > eip {
l = m + 1
} else {
dataPtr = getLong(this.dbBinStr, p + 8)
break;
dataPtr = getLong(region.dbBinStr, p+8)
break
}
}
}
@@ -134,26 +134,26 @@ func (this *Ip2Region) MemorySearch(ipStr string) (ipInfo IpInfo, err error) {
}
dataLen := ((dataPtr >> 24) & 0xFF)
dataPtr = (dataPtr & 0x00FFFFFF);
ipInfo = getIpInfo(getLong(this.dbBinStr, dataPtr), this.dbBinStr[(dataPtr) + 4:dataPtr + dataLen])
dataPtr = (dataPtr & 0x00FFFFFF)
ipInfo = getIpInfo(getLong(region.dbBinStr, dataPtr), region.dbBinStr[(dataPtr)+4:dataPtr+dataLen])
return ipInfo, nil
}
func (this *Ip2Region)BinarySearch(ipStr string) (ipInfo IpInfo, err error) {
func (region *Ip2Region) BinarySearch(ipStr string) (ipInfo IpInfo, err error) {
ipInfo = IpInfo{}
if this.totalBlocks == 0 {
this.dbFileHandler.Seek(0, 0)
if region.totalBlocks == 0 {
region.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
region.dbFileHandler.Read(superBlock)
region.firstIndexPtr = getLong(superBlock, 0)
region.lastIndexPtr = getLong(superBlock, 4)
region.totalBlocks = (region.lastIndexPtr-region.firstIndexPtr)/INDEX_BLOCK_LENGTH + 1
}
var l, dataPtr, p int64
h := this.totalBlocks
h := region.totalBlocks
ip, err := ip2long(ipStr)
@@ -161,21 +161,21 @@ func (this *Ip2Region)BinarySearch(ipStr string) (ipInfo IpInfo, err error) {
return
}
for (l <= h) {
for l <= h {
m := (l + h) >> 1
p = m * INDEX_BLOCK_LENGTH
_, err = this.dbFileHandler.Seek(this.firstIndexPtr + p, 0)
_, err = region.dbFileHandler.Seek(region.firstIndexPtr+p, 0)
if err != nil {
return
}
buffer := make([]byte, INDEX_BLOCK_LENGTH)
_, err = this.dbFileHandler.Read(buffer)
_, err = region.dbFileHandler.Read(buffer)
if err != nil {
return ipInfo, err
}
sip := getLong(buffer, 0)
if ip < sip {
@@ -186,7 +186,7 @@ func (this *Ip2Region)BinarySearch(ipStr string) (ipInfo IpInfo, err error) {
l = m + 1
} else {
dataPtr = getLong(buffer, 8)
break;
break
}
}
@@ -198,76 +198,76 @@ func (this *Ip2Region)BinarySearch(ipStr string) (ipInfo IpInfo, err error) {
}
dataLen := ((dataPtr >> 24) & 0xFF)
dataPtr = (dataPtr & 0x00FFFFFF);
dataPtr = (dataPtr & 0x00FFFFFF)
this.dbFileHandler.Seek(dataPtr, 0)
region.dbFileHandler.Seek(dataPtr, 0)
data := make([]byte, dataLen)
this.dbFileHandler.Read(data)
region.dbFileHandler.Read(data)
ipInfo = getIpInfo(getLong(data, 0), data[4:dataLen])
err = nil
return
}
func (this *Ip2Region) BtreeSearch(ipStr string) (ipInfo IpInfo, err error) {
func (region *Ip2Region) BtreeSearch(ipStr string) (ipInfo IpInfo, err error) {
ipInfo = IpInfo{}
ip, err := ip2long(ipStr)
if this.headerLen == 0 {
this.dbFileHandler.Seek(8, 0)
if region.headerLen == 0 {
region.dbFileHandler.Seek(8, 0)
buffer := make([]byte, TOTAL_HEADER_LENGTH)
this.dbFileHandler.Read(buffer)
var idx int64;
region.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))
dataPar := getLong(buffer, int64(i+4))
if dataPar == 0 {
break
}
this.headerSip = append(this.headerSip, startIp)
this.headerPtr = append(this.headerPtr, dataPar)
idx ++;
region.headerSip = append(region.headerSip, startIp)
region.headerPtr = append(region.headerPtr, dataPar)
idx++
}
this.headerLen = idx
region.headerLen = idx
}
var l, sptr, eptr int64
h := this.headerLen
h := region.headerLen
for l <= h {
m := int64(l + h) >> 1
if m < this.headerLen {
if ip == this.headerSip[m] {
m := int64(l+h) >> 1
if m < region.headerLen {
if ip == region.headerSip[m] {
if m > 0 {
sptr = this.headerPtr[m - 1]
eptr = this.headerPtr[m]
sptr = region.headerPtr[m-1]
eptr = region.headerPtr[m]
} else {
sptr = this.headerPtr[m]
eptr = this.headerPtr[m + 1]
sptr = region.headerPtr[m]
eptr = region.headerPtr[m+1]
}
break
}
if ip < this.headerSip[m] {
if ip < region.headerSip[m] {
if m == 0 {
sptr = this.headerPtr[m]
eptr = this.headerPtr[m + 1]
sptr = region.headerPtr[m]
eptr = region.headerPtr[m+1]
break
} else if ip > this.headerSip[m - 1] {
sptr = this.headerPtr[m - 1]
eptr = this.headerPtr[m]
} else if ip > region.headerSip[m-1] {
sptr = region.headerPtr[m-1]
eptr = region.headerPtr[m]
break
}
h = m - 1
} else {
if m == this.headerLen - 1 {
sptr = this.headerPtr[m - 1]
eptr = this.headerPtr[m]
if m == region.headerLen-1 {
sptr = region.headerPtr[m-1]
eptr = region.headerPtr[m]
break
} else if ip <= this.headerSip[m + 1] {
sptr = this.headerPtr[m ]
eptr = this.headerPtr[m + 1]
} else if ip <= region.headerSip[m+1] {
sptr = region.headerPtr[m]
eptr = region.headerPtr[m+1]
break
}
l = m + 1
@@ -282,25 +282,25 @@ func (this *Ip2Region) BtreeSearch(ipStr string) (ipInfo IpInfo, err error) {
}
blockLen := eptr - sptr
this.dbFileHandler.Seek(sptr, 0)
index := make([]byte, blockLen +INDEX_BLOCK_LENGTH)
this.dbFileHandler.Read(index)
region.dbFileHandler.Seek(sptr, 0)
index := make([]byte, blockLen+INDEX_BLOCK_LENGTH)
region.dbFileHandler.Read(index)
var dataptr int64
h = blockLen / INDEX_BLOCK_LENGTH
l = 0
for l <= h {
m := int64(l + h) >> 1
m := int64(l+h) >> 1
p := m * INDEX_BLOCK_LENGTH
sip := getLong(index, p)
if ip < sip {
h = m - 1;
h = m - 1
} else {
eip := getLong(index, p + 4)
eip := getLong(index, p+4)
if ip > eip {
l = m + 1
} else {
dataptr = getLong(index, p + 8)
dataptr = getLong(index, p+8)
break
}
}
@@ -314,19 +314,19 @@ func (this *Ip2Region) BtreeSearch(ipStr string) (ipInfo IpInfo, err error) {
dataLen := (dataptr >> 24) & 0xFF
dataPtr := dataptr & 0x00FFFFFF
this.dbFileHandler.Seek(dataPtr, 0)
region.dbFileHandler.Seek(dataPtr, 0)
data := make([]byte, dataLen)
this.dbFileHandler.Read(data)
region.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)
val := (int64(b[offset]) |
int64(b[offset+1])<<8 |
int64(b[offset+2])<<16 |
int64(b[offset+3])<<24)
return val
@@ -341,9 +341,8 @@ func ip2long(IpStr string) (int64, error) {
var sum int64
for i, n := range bits {
bit, _ := strconv.ParseInt(n, 10, 64)
sum += bit << uint(24 - 8 * i)
sum += bit << uint(24-8*i)
}
return sum, nil
}

View File

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

View File

@@ -1,4 +1,4 @@
package util
package logger
import (
"github.com/natefinch/lumberjack"
@@ -6,12 +6,12 @@ import (
"go.uber.org/zap/zapcore"
)
func NewLogger() *zap.SugaredLogger {
writeSyncer := getLogWriter()
encoder := getEncoder()
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
var Logger *zap.SugaredLogger
func NewLogger() {
core := zapcore.NewCore(getEncoder(), getLogWriter(), zapcore.DebugLevel)
logger := zap.New(core, zap.AddCaller())
return logger.Sugar()
Logger = logger.Sugar()
}
func getEncoder() zapcore.Encoder {

View File

@@ -1,12 +1,9 @@
package util
package random
import (
"fmt"
"math/rand"
"strings"
"time"
gonanoid "github.com/matoous/go-nanoid"
)
const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
@@ -29,23 +26,10 @@ func RandomString(n int) string {
func RandomInt(n int) string {
var letters = []byte("0123456789")
l := len(letters)
result := make([]byte, n)
for i := range result {
result[i] = letters[rand.Intn(len(letters))]
result[i] = letters[rand.Intn(l)]
}
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
}

View File

@@ -1,8 +1,7 @@
package util
package st
import (
"regexp"
"strconv"
"strings"
)
@@ -48,12 +47,8 @@ func RemoveHTML(str string) string {
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)
}

View File

@@ -24,14 +24,15 @@ func NewJWTMaker(secretKey string) (Maker, error) {
}
// CreateToken 根据用户名和时间创建一个新的token
func (maker *JWTMaker) CreateToken(username string, duration time.Duration) (string, error) {
payload, err := NewPayload(username, duration)
func (maker *JWTMaker) CreateToken(id string, username string, duration time.Duration) (string, *Payload, error) {
payload, err := NewPayload(id, username, duration)
if err != nil {
return "", err
return "", payload, err
}
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, payload)
return jwtToken.SignedString([]byte(maker.secretKey))
token, err := jwtToken.SignedString([]byte(maker.secretKey))
return token, payload, err
}
// VerifyToken checks if the token is valid or not

View File

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

View File

@@ -29,13 +29,14 @@ func NewPasetoMaker(symmetricKey string) (Maker, error) {
}
// 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)
func (maker *PasetoMaker) CreateToken(id string, username string, duration time.Duration) (string, *Payload, error) {
payload, err := NewPayload(id, username, duration)
if err != nil {
return "", err
return "", payload, err
}
return maker.paseto.Encrypt(maker.symmetricKey, payload, nil)
token, err := maker.paseto.Encrypt(maker.symmetricKey, payload, nil)
return token, payload, err
}
// VerifyToken checks if the token is valid or not

View File

@@ -3,8 +3,6 @@ package token
import (
"errors"
"time"
"github.com/google/uuid"
)
// Different types of error returned by the VerifyToken function
@@ -15,21 +13,16 @@ var (
// Payload contains the payload data of the token
type Payload struct {
ID uuid.UUID `json:"id"`
ID string `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
}
func NewPayload(id string, username string, duration time.Duration) (*Payload, error) {
payload := &Payload{
ID: tokenID,
ID: id,
Username: username,
IssuedAt: time.Now(),
ExpiredAt: time.Now().Add(duration),

View File

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

View File

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

View File

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

View File

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

21
pkg/validator/phone.go Normal file
View File

@@ -0,0 +1,21 @@
package validator
import (
"errors"
"regexp"
"strings"
)
var (
rxPhone = regexp.MustCompile(`^(13|14|15|16|17|18|19)\d{9}$`)
ErrPhoneFormat = errors.New("phone format error")
)
func ValidateRxPhone(phone string) error {
phone = strings.TrimSpace(phone)
if !rxPhone.MatchString(phone) {
return ErrPhoneFormat
}
return nil
}