forked from all_user/stat_center
Initial commit
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
//ToBsonM 将struct转换为 Bson Map
|
||||
func ToBsonM(s interface{}) (bson.M, error) {
|
||||
data, err := bson.Marshal(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(bson.M)
|
||||
if err := bson.Unmarshal(data, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// 将page转换为数据库的skip、limit
|
||||
func ToDBPage(pageNum int64, pageSize int64) (skip int64, limit int64) {
|
||||
return (pageNum - 1) * pageSize, pageSize
|
||||
}
|
||||
|
||||
//JSONStruct2Map 将struct转换为Map
|
||||
func JSONStruct2Map(obj interface{}) (map[string]interface{}, error) {
|
||||
m := make(map[string]interface{})
|
||||
j, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(j, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, err
|
||||
}
|
||||
|
||||
//Map2JSONStruct 将map转换为Json struct
|
||||
func Map2JSONStruct(v interface{}, m map[string]interface{}) error {
|
||||
j, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(j, v); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// struct转换为Map
|
||||
func Struct2Map(obj interface{}) map[string]interface{} {
|
||||
m := make(map[string]interface{})
|
||||
b, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return m
|
||||
}
|
||||
if err = json.Unmarshal(b, &m); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
var _client *http.Client
|
||||
|
||||
func GetHttpClient() *http.Client {
|
||||
once.Do(func() {
|
||||
_client = &http.Client{
|
||||
Timeout: time.Minute * 1, //设置超时时间
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: 10 * time.Second, //限制 TLS握手的时间
|
||||
ResponseHeaderTimeout: 10 * time.Second, //限制读取response header的时间,默认 timeout + 5*time.Second
|
||||
ExpectContinueTimeout: 1 * time.Second, //限制client在发送包含 Expect: 100-continue的header到收到继续发送body的response之间的时间等待。
|
||||
MaxIdleConns: 100, //所有host的连接池最大连接数量,默认无穷大
|
||||
MaxIdleConnsPerHost: 30, //每个host的连接池最大空闲连接数,默认2
|
||||
MaxConnsPerHost: 10, //每个host的最大连接数量
|
||||
IdleConnTimeout: 3 * time.Minute, //how long an idle connection is kept in the connection pool.
|
||||
},
|
||||
}
|
||||
})
|
||||
return _client
|
||||
}
|
||||
|
||||
func getBytes(request interface{}) ([]byte, error) {
|
||||
var body []byte
|
||||
switch t := request.(type) {
|
||||
case []byte:
|
||||
body = t
|
||||
case string:
|
||||
body = []byte(t)
|
||||
default:
|
||||
var err error
|
||||
body, err = json.Marshal(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
type OnHttpResponse func(res *http.Response) error
|
||||
|
||||
func HttpRequest(req *http.Request, onResponse OnHttpResponse) error {
|
||||
client := GetHttpClient()
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if onResponse != nil {
|
||||
err = onResponse(res)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func HttpMethod(url, method string, header http.Header, body io.Reader, onResponse OnHttpResponse) error {
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if header != nil {
|
||||
req.Header = header
|
||||
}
|
||||
return HttpRequest(req, onResponse)
|
||||
}
|
||||
|
||||
func HttpGet(url string, header http.Header, body io.Reader, onResponse OnHttpResponse) error {
|
||||
return HttpMethod(url, http.MethodGet, header, body, onResponse)
|
||||
}
|
||||
|
||||
func HttpPost(url string, header http.Header, body io.Reader, onResponse OnHttpResponse) error {
|
||||
return HttpMethod(url, http.MethodPost, header, body, onResponse)
|
||||
}
|
||||
|
||||
func HttpPostRequestJson(url string, request interface{}, resp interface{}) error {
|
||||
header := make(http.Header)
|
||||
header.Set("Content-Type", "application/json")
|
||||
body, err := getBytes(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return HttpPost(url, header, bytes.NewReader(body), func(res *http.Response) error {
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("http request (%s) error %s", url, res.Status)
|
||||
}
|
||||
content, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(content, resp)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
AdminAccountKey = "AdminAccount"
|
||||
AccountTypeKey = "AccountType"
|
||||
)
|
||||
|
||||
//CreateToken 生成Token算法
|
||||
func CreateToken(secret string, tokenClaims map[string]interface{}) (tokeness string, err error) {
|
||||
if secret == "" {
|
||||
return "", errors.New("secret is empty")
|
||||
}
|
||||
claims := jwt.MapClaims(tokenClaims)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokeness, err = token.SignedString([]byte(secret))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tokeness, nil
|
||||
}
|
||||
|
||||
func keyFunc(secret string) jwt.Keyfunc {
|
||||
return func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(secret), nil
|
||||
}
|
||||
}
|
||||
|
||||
//ParseToken 解析Token
|
||||
func ParseToken(secret string, tokeness string) (map[string]interface{}, error) {
|
||||
if secret == "" || tokeness == "" {
|
||||
return nil, errors.New("secret or tokeness is empty")
|
||||
}
|
||||
token, err := jwt.Parse(tokeness, keyFunc(secret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//验证token,如果token被修改过则为false
|
||||
if !token.Valid {
|
||||
return nil, errors.New("token is invalid")
|
||||
}
|
||||
claimsToken, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
return nil, errors.New("cannot convert claim to MapClaim")
|
||||
}
|
||||
return claimsToken, nil
|
||||
}
|
||||
|
||||
// GetAdmin 获取管理员账号
|
||||
// oauth.Auth执行后有效
|
||||
func GetAdmin(ctx *gin.Context) (account string, err error) {
|
||||
val, exists := ctx.Get(AdminAccountKey)
|
||||
if !exists {
|
||||
err = errors.New("admin account not exist")
|
||||
return
|
||||
}
|
||||
|
||||
account, ok := val.(string)
|
||||
if !ok {
|
||||
return "", errors.New("admin account type error")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetAccountType 获取账号类型
|
||||
// oauth.Auth执行后有效
|
||||
func GetAccountType(ctx *gin.Context) (string, error) {
|
||||
var err error
|
||||
val, exists := ctx.Get(AccountTypeKey)
|
||||
if !exists {
|
||||
err = errors.New("claims not exist")
|
||||
return "", err
|
||||
}
|
||||
accType, ok := val.(string)
|
||||
if !ok {
|
||||
return "", errors.New("claims type error")
|
||||
}
|
||||
return accType, nil
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ExpiredLock struct {
|
||||
items map[string]time.Time
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewExpiredLock() *ExpiredLock {
|
||||
return &ExpiredLock{
|
||||
items: make(map[string]time.Time),
|
||||
}
|
||||
}
|
||||
|
||||
func (kl *ExpiredLock) Count() int64 {
|
||||
kl.lock.RLock()
|
||||
defer kl.lock.RUnlock()
|
||||
return int64(len(kl.items))
|
||||
}
|
||||
|
||||
func (kl *ExpiredLock) LockedCount() int64 {
|
||||
kl.lock.RLock()
|
||||
defer kl.lock.RUnlock()
|
||||
now := time.Now()
|
||||
count := int64(0)
|
||||
for _, expired := range kl.items {
|
||||
if now.Before(expired) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (kl *ExpiredLock) UnLock(key string, expired time.Time) {
|
||||
kl.lock.Lock()
|
||||
defer kl.lock.Unlock()
|
||||
expire, exists := kl.items[key]
|
||||
if !exists || expire != expired {
|
||||
return
|
||||
}
|
||||
delete(kl.items, key)
|
||||
}
|
||||
|
||||
func (kl *ExpiredLock) TryLock(key string, expired time.Time) bool {
|
||||
kl.lock.Lock()
|
||||
defer kl.lock.Unlock()
|
||||
expire, exists := kl.items[key]
|
||||
if !exists {
|
||||
kl.items[key] = expired
|
||||
return true
|
||||
} else {
|
||||
if time.Now().After(expire) {
|
||||
kl.items[key] = expired
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func RandDigits(n uint) string {
|
||||
s := ""
|
||||
for n != 0 {
|
||||
s += fmt.Sprintf("%d", rand.Intn(10))
|
||||
n--
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
const lowerLetter = "abcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
func RandLowerString(n int) string {
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = lowerLetter[rand.Intn(len(lowerLetter))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func GetRandomUserName(base string, len int) string {
|
||||
return base + RandDigits(2) + RandLowerString(len)
|
||||
}
|
||||
|
||||
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
func RandStringBytes(n int) string {
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PathInfo 路径信息
|
||||
type PathInfo struct {
|
||||
Dir string // 目录
|
||||
Ext string // 扩展名
|
||||
Filename string // 没有扩展名的文件名名称
|
||||
FullFileName string // 文件全名
|
||||
}
|
||||
|
||||
// PathForInfo 解析路径
|
||||
// 返回文件,路径,文件名等信息
|
||||
func PathForInfo(path string) (pi PathInfo) {
|
||||
if path == "" {
|
||||
return
|
||||
}
|
||||
ext := filepath.Ext(path)
|
||||
dir, name := filepath.Split(path)
|
||||
|
||||
pi.Dir = dir
|
||||
pi.Ext = ext
|
||||
pi.Filename = strings.TrimRight(name, ext)
|
||||
pi.FullFileName = name
|
||||
return
|
||||
}
|
||||
|
||||
func GetFileSize(filepath string) (int64, error) {
|
||||
fi, err := os.Stat(filepath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// get the size
|
||||
return fi.Size(), nil
|
||||
}
|
||||
|
||||
// JoinUrlPath 域名与uri拼接
|
||||
func JoinUrlPath(host string, uri string) (urlpath string) {
|
||||
switch {
|
||||
case strings.HasSuffix(host, "/") && strings.HasPrefix(uri, "/"):
|
||||
urlpath = host[0:len(host)-1] + uri
|
||||
case !strings.HasSuffix(host, "/") && !strings.HasPrefix(uri, "/"):
|
||||
urlpath = host + "/" + uri
|
||||
default:
|
||||
urlpath = host + uri
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package utils
|
||||
|
||||
// 定义一个集合接口
|
||||
type ISet[T comparable] interface {
|
||||
Add(...T) // 增加集合元素
|
||||
Remove(...T) // 删除集合元素
|
||||
Contains(T) bool // 集合是否包含元素
|
||||
Inter(ISet[T]) ISet[T] // 集合求交集
|
||||
Union(ISet[T]) ISet[T] // 集合求合集
|
||||
Diff(ISet[T]) ISet[T] // 集合求差集
|
||||
Equal(ISet[T]) bool // 集合是否相等
|
||||
Traver(func(T)) // 遍历集合元素
|
||||
Elements() []T // 获取集合所有元素列表
|
||||
IsEmpty() bool // 集合是否为空
|
||||
}
|
||||
|
||||
// Set 使用字典实现集合
|
||||
type Set[T comparable] struct {
|
||||
dict map[T]struct{}
|
||||
}
|
||||
|
||||
func NewSet[T comparable](src []T) ISet[T] {
|
||||
ret := &Set[T]{dict: make(map[T]struct{})}
|
||||
for _, t := range src {
|
||||
ret.dict[t] = struct{}{}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Add 实现集合接口
|
||||
func (s *Set[T]) Add(items ...T) {
|
||||
for _, t := range items {
|
||||
s.dict[t] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Set[T]) Remove(items ...T) {
|
||||
for _, t := range items {
|
||||
delete(s.dict, t)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Set[T]) Contains(item T) bool {
|
||||
if _, ok := s.dict[item]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Set[T]) Inter(other ISet[T]) ISet[T] {
|
||||
result := &Set[T]{
|
||||
dict: make(map[T]struct{}),
|
||||
}
|
||||
for k, _ := range s.dict {
|
||||
if other.Contains(k) {
|
||||
result.Add(k)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *Set[T]) Union(other ISet[T]) ISet[T] {
|
||||
result := &Set[T]{
|
||||
dict: make(map[T]struct{}),
|
||||
}
|
||||
s.Traver(func(item T) {
|
||||
result.Add(item)
|
||||
})
|
||||
other.Traver(func(item T) {
|
||||
result.Add(item)
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *Set[T]) Diff(other ISet[T]) ISet[T] {
|
||||
result := &Set[T]{
|
||||
dict: make(map[T]struct{}),
|
||||
}
|
||||
s.Traver(func(item T) {
|
||||
if !other.Contains(item) {
|
||||
result.Add(item)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *Set[T]) Equal(other ISet[T]) bool {
|
||||
// 如果 (A - B) 和 (B - A) 都是空集,则说明两个集合的元素完全相同,因此它们相等
|
||||
d1 := s.Diff(other)
|
||||
d2 := other.Diff(s)
|
||||
return d1.IsEmpty() && d2.IsEmpty()
|
||||
}
|
||||
|
||||
func (s *Set[T]) Traver(onItem func(item T)) {
|
||||
for k, _ := range s.dict {
|
||||
onItem(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Set[T]) Elements() []T {
|
||||
ret := make([]T, 0)
|
||||
for k, _ := range s.dict {
|
||||
ret = append(ret, k)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *Set[T]) IsEmpty() bool {
|
||||
return len(s.dict) == 0
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package utils
|
||||
|
||||
import "strings"
|
||||
|
||||
func RepairDC(dc string) string {
|
||||
ret := strings.ReplaceAll(dc, "*", "")
|
||||
return strings.TrimSpace(ret)
|
||||
}
|
||||
|
||||
func TowIsOR(v1, v2 string, isV string) bool {
|
||||
return strings.ToUpper(v1) == strings.ToUpper(isV) || strings.ToUpper(v2) == strings.ToUpper(isV)
|
||||
}
|
||||
func TowIsORs(v1, v2 string, isVs []string) bool {
|
||||
v1 = strings.ToUpper(v1)
|
||||
v2 = strings.ToUpper(v2)
|
||||
for _, v := range isVs {
|
||||
vU := strings.ToUpper(v)
|
||||
if v1 == vU || v2 == vU {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
func ThreeIsOR(v1, v2, v3 string, isV string) bool {
|
||||
return strings.ToUpper(v1) == strings.ToUpper(isV) || strings.ToUpper(v2) == strings.ToUpper(isV) || strings.ToUpper(v3) == strings.ToUpper(isV)
|
||||
}
|
||||
|
||||
type StrSliceConstructFilter func(index int) (string, bool) // 返回 是否保留,项值
|
||||
func StrSliceConstruct(length int, filter StrSliceConstructFilter) []string {
|
||||
ret := make([]string, 0)
|
||||
if filter == nil {
|
||||
return ret
|
||||
}
|
||||
for i := 0; i < length; i++ {
|
||||
if item, keep := filter(i); keep {
|
||||
ret = append(ret, item)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStrSliceConstruct(t *testing.T) {
|
||||
type args struct {
|
||||
length int
|
||||
filter StrSliceConstructFilter
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "1",
|
||||
args: args{
|
||||
length: 5,
|
||||
filter: func(index int) (string, bool) {
|
||||
return fmt.Sprintf("%d", index), true
|
||||
},
|
||||
},
|
||||
want: []string{"0", "1", "2", "3", "4"},
|
||||
},
|
||||
{
|
||||
name: "2",
|
||||
args: args{
|
||||
length: 5,
|
||||
filter: func(index int) (string, bool) {
|
||||
return fmt.Sprintf("%d", index*index), true
|
||||
},
|
||||
},
|
||||
want: []string{"0", "1", "4", "9", "16"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := StrSliceConstruct(tt.args.length, tt.args.filter); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("StrSliceConstruct() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type WaitGroup struct {
|
||||
wg sync.WaitGroup
|
||||
fs []func()
|
||||
}
|
||||
|
||||
func NewWaitGroup() *WaitGroup {
|
||||
wg := &WaitGroup{}
|
||||
wg.fs = make([]func(), 0)
|
||||
return wg
|
||||
}
|
||||
|
||||
func (w *WaitGroup) Add(f ...func()) *WaitGroup {
|
||||
if w == nil {
|
||||
panic("WaitGroup can not be null\n")
|
||||
}
|
||||
if w.fs == nil {
|
||||
panic("WaitGroup may be not init\n")
|
||||
}
|
||||
w.fs = append(w.fs, f...)
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *WaitGroup) RunWaits() {
|
||||
w.wg.Add(len(w.fs))
|
||||
for _, f := range w.fs {
|
||||
go func(wg *sync.WaitGroup, fn func()) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
fn()
|
||||
}(&w.wg, f)
|
||||
}
|
||||
w.wg.Wait()
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/now"
|
||||
)
|
||||
|
||||
const (
|
||||
DateISO = "2006-01-02"
|
||||
TimeISO = "2006-01-02 15:04:05"
|
||||
)
|
||||
|
||||
var (
|
||||
LocBJ, _ = time.LoadLocation("Asia/Shanghai")
|
||||
)
|
||||
|
||||
func init() {
|
||||
time.Local = LocBJ
|
||||
}
|
||||
|
||||
func Now() time.Time {
|
||||
return time.Now().Local()
|
||||
}
|
||||
|
||||
func NowMillisecond() int64 {
|
||||
return Now().UnixNano() / int64(time.Millisecond)
|
||||
}
|
||||
|
||||
func BeginOfYesterday() time.Time {
|
||||
t := Now().AddDate(0, 0, -1)
|
||||
year, month, day := t.Date()
|
||||
return time.Date(year, month, day, 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
func BeginOfYesterday0() time.Time {
|
||||
t := Now().AddDate(0, 0, -2)
|
||||
year, month, day := t.Date()
|
||||
return time.Date(year, month, day, 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
func BeginOfToday() time.Time {
|
||||
t := Now()
|
||||
year, month, day := t.Date()
|
||||
return time.Date(year, month, day, 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
func BeginOfHour() time.Time {
|
||||
t := Now()
|
||||
year, month, day := t.Date()
|
||||
return time.Date(year, month, day, t.Hour(), 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
func EndOfToday() time.Time {
|
||||
t := Now()
|
||||
year, month, day := t.Date()
|
||||
return time.Date(year, month, day, 23, 59, 59, 0, t.Location())
|
||||
}
|
||||
|
||||
func BeginTodayMillsecond() int64 {
|
||||
return BeginOfToday().UnixNano() / int64(time.Millisecond)
|
||||
}
|
||||
|
||||
// ServerCurrMillisecond 获得时间毫秒数 13位
|
||||
func ServerCurrMillisecond(t time.Time) int64 {
|
||||
return t.UnixNano() / 1000000
|
||||
}
|
||||
|
||||
func BeginWithTime(t time.Time) time.Time {
|
||||
year, month, day := t.Date()
|
||||
return time.Date(year, month, day, 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
func BeginMonthWithTime(t time.Time) time.Time {
|
||||
year, month, _ := t.Date()
|
||||
return time.Date(year, month, 1, 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
func BeginWithTimeLocal(t time.Time) time.Time {
|
||||
localTime := t.Local()
|
||||
year, month, day := localTime.Date()
|
||||
return time.Date(year, month, day, 0, 0, 0, 0, time.Local)
|
||||
}
|
||||
|
||||
func EndWithTime(t time.Time) time.Time {
|
||||
year, month, day := t.Date()
|
||||
return time.Date(year, month, day, 23, 59, 59, 0, t.Location())
|
||||
}
|
||||
|
||||
func IsToday(t time.Time) bool {
|
||||
if t.IsZero() {
|
||||
return false
|
||||
}
|
||||
today := Now()
|
||||
|
||||
return t.Year() == today.Year() &&
|
||||
t.Month() == today.Month() &&
|
||||
t.Day() == today.Day()
|
||||
}
|
||||
|
||||
// BeginningOfTomorrow 明天的开始
|
||||
func BeginningOfTomorrow(t time.Time) time.Time {
|
||||
return BeginningOfDay(t).AddDate(0, 0, 1)
|
||||
}
|
||||
|
||||
// BeginningOfDay 一天的开始
|
||||
func BeginningOfDay(t time.Time) time.Time {
|
||||
return now.New(t).BeginningOfDay()
|
||||
}
|
||||
|
||||
// StrTimeToTime 时间字符串转时间
|
||||
func StrTimeToTime(strTime string) (t time.Time) {
|
||||
timeLayout := "2006-01-02T15:04:05-07:00"
|
||||
loc, _ := time.LoadLocation("Local")
|
||||
t, _ = time.ParseInLocation(timeLayout, strTime, loc)
|
||||
return
|
||||
}
|
||||
|
||||
func RecentMinute(tim time.Time, scaleMinute int64) time.Time {
|
||||
minute := int64(tim.Minute())
|
||||
lave := minute % scaleMinute
|
||||
formatMinute := minute - lave
|
||||
return time.Date(tim.Year(), tim.Month(), tim.Day(), tim.Hour(), int(formatMinute), 0, 0, tim.Location())
|
||||
}
|
||||
|
||||
var (
|
||||
GlobalShardHour = time.Hour
|
||||
GlobalShardDay = 24 * time.Hour
|
||||
GlobalShardWeek = 7 * 24 * time.Hour
|
||||
GlobalShardMonth = 30 * 24 * time.Hour
|
||||
DefaultStatBeginTime, _ = time.ParseInLocation("2006-01-02", "2021-03-01", time.Local)
|
||||
)
|
||||
|
||||
type TimeSegment struct {
|
||||
Begin time.Time
|
||||
End time.Time
|
||||
}
|
||||
type TimeSegmentList []TimeSegment
|
||||
|
||||
func CalcTimeSegments(start, end time.Time, step time.Duration) TimeSegmentList {
|
||||
ret := make(TimeSegmentList, 0)
|
||||
if start.After(end) {
|
||||
return ret
|
||||
}
|
||||
if start.Add(step).After(end) {
|
||||
ret = append(ret, TimeSegment{
|
||||
Begin: start,
|
||||
End: end,
|
||||
})
|
||||
return ret
|
||||
}
|
||||
b := start
|
||||
for b.Before(end) {
|
||||
e := b.Add(step)
|
||||
if e.After(end) {
|
||||
e = end
|
||||
}
|
||||
ret = append(ret, TimeSegment{Begin: b, End: e})
|
||||
b = b.Add(step)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func BeginOfMonth(t time.Time) time.Time {
|
||||
year, month, _ := t.In(time.Local).Date()
|
||||
return time.Date(year, month, 1, 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
func EndOfMonth(t time.Time) time.Time {
|
||||
return BeginOfMonth(t).AddDate(0, 1, 0).Add(-time.Nanosecond)
|
||||
}
|
||||
|
||||
func MonthDateRange(t time.Time) (string, string) {
|
||||
b := BeginOfMonth(t)
|
||||
e := EndOfMonth(t)
|
||||
return GetDateString(b), GetDateString(e)
|
||||
}
|
||||
|
||||
func GetDateString(t time.Time) string {
|
||||
return t.In(LocBJ).Format(DateISO)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRecentMinute(t *testing.T) {
|
||||
time.Local, _ = time.LoadLocation("Asia/Shanghai")
|
||||
start, _ := time.ParseInLocation(time.RFC3339, "2023-07-28T00:00:00.000+08:00", time.Local)
|
||||
end, _ := time.ParseInLocation(time.RFC3339, "2023-07-28T00:07:00.000+08:00", time.Local)
|
||||
t.Log(start, end)
|
||||
list := CalcTimeSegments(start, end, GlobalShardHour)
|
||||
for _, item := range list {
|
||||
t.Log(item.Begin, item.End)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.org/x/exp/constraints"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"stat/common/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func RecoverError(onError func(err error)) {
|
||||
if r := recover(); r != nil {
|
||||
err := fmt.Errorf("internal error: %v", r)
|
||||
if onError != nil {
|
||||
onError(err)
|
||||
}
|
||||
log.Error(fmt.Sprintf("[Panic] RecoverError err: %+v\n", r) + string(debug.Stack()))
|
||||
}
|
||||
}
|
||||
|
||||
/*func PValue[T any](value T) *T {
|
||||
ret := value
|
||||
return &ret
|
||||
}
|
||||
*/
|
||||
|
||||
type M map[string]interface{}
|
||||
|
||||
func (m M) HasKey(key string) bool {
|
||||
if _, ok := m[key]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type OnPage func(skip int64, limit int64) error
|
||||
|
||||
func Abs[T constraints.Integer | constraints.Float](v T) T {
|
||||
if v < 0 {
|
||||
return -v
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func EnsureRange[T constraints.Ordered](value, min, max T) T {
|
||||
if value < min {
|
||||
value = min
|
||||
}
|
||||
if value > max {
|
||||
value = max
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func InRange[T constraints.Ordered](value, min, max T) bool {
|
||||
if value < min {
|
||||
return false
|
||||
}
|
||||
if value > max {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TimeIsBetween(t, min, max time.Time, includeEndPoint bool) bool {
|
||||
if includeEndPoint {
|
||||
return (t.Equal(min) || t.After(min)) && (t.Equal(max) || t.Before(max))
|
||||
} else {
|
||||
return (t.Equal(min) || t.After(min)) && t.Before(max)
|
||||
}
|
||||
}
|
||||
|
||||
func SlicePaged[T any](slice []T, chunkSize int, onPiece func(piece []T) error) error {
|
||||
array := SplitSlice(slice, chunkSize)
|
||||
for _, arr := range array {
|
||||
if err := onPiece(arr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CountPaged(totalCount int64, pageSize int64, onPage OnPage) error {
|
||||
pageTotal := int(totalCount / pageSize)
|
||||
if totalCount%pageSize != 0 {
|
||||
pageTotal++
|
||||
}
|
||||
for page := 0; page < pageTotal; page++ {
|
||||
if err := onPage(int64(page)*pageSize, pageSize); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SplitSlice[T any](s []T, chunkSize int) [][]T {
|
||||
if chunkSize <= 0 {
|
||||
return nil
|
||||
}
|
||||
var chunks [][]T
|
||||
for i := 0; i < len(s); i += chunkSize {
|
||||
end := i + chunkSize
|
||||
if end > len(s) {
|
||||
end = len(s)
|
||||
}
|
||||
chunks = append(chunks, s[i:end])
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
func GetSlicePageRange[T any](s []T, pageNum, pageSize int) []T {
|
||||
totalLen := len(s)
|
||||
start, end := GetPageRange(totalLen, pageNum, pageSize)
|
||||
return s[start:end]
|
||||
}
|
||||
|
||||
func GetPageRange(totalLen, pageNum, pageSize int) (start, end int) {
|
||||
if pageNum < 1 {
|
||||
pageNum = 1
|
||||
}
|
||||
if pageSize < 1 {
|
||||
pageSize = 10
|
||||
}
|
||||
start = (pageNum - 1) * pageSize
|
||||
if start > totalLen {
|
||||
start = totalLen
|
||||
}
|
||||
end = start + pageSize
|
||||
if end > totalLen {
|
||||
end = totalLen
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func IsSliceEqual[T constraints.Ordered](src []T, dst []T) bool {
|
||||
sort.Slice(src, func(i, j int) bool { return src[i] < src[j] })
|
||||
sort.Slice(dst, func(i, j int) bool { return dst[i] < dst[j] })
|
||||
if len(src) != len(dst) {
|
||||
return false
|
||||
}
|
||||
for i := range src {
|
||||
if src[i] != dst[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package utils
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
func UUID() string {
|
||||
return uuid.New().String()
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const TotalRows = 1048576
|
||||
|
||||
const MinColumns = 1
|
||||
|
||||
const MaxColumns = 16384
|
||||
|
||||
// ColumnNumberToName provides a function to convert the integer to Excel
|
||||
// sheet column title.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// excelize.ColumnNumberToName(37) // returns "AK", nil
|
||||
func ColumnNumberToName(num int) (string, error) {
|
||||
if num < MinColumns || num > MaxColumns {
|
||||
return "", fmt.Errorf("the column number must be greater than or equal to %d and less than or equal to %d", MinColumns, MaxColumns)
|
||||
}
|
||||
var col string
|
||||
for num > 0 {
|
||||
col = string(rune((num-1)%26+65)) + col
|
||||
num = (num - 1) / 26
|
||||
}
|
||||
return col, nil
|
||||
}
|
||||
|
||||
// CoordinatesToCellName converts [X, Y] coordinates to alpha-numeric cell
|
||||
// name or returns an error.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// excelize.CoordinatesToCellName(1, 1) // returns "A1", nil
|
||||
// excelize.CoordinatesToCellName(1, 1, true) // returns "$A$1", nil
|
||||
func CoordinatesToCellName(col, row int, abs ...bool) (string, error) {
|
||||
if col < 1 || row < 1 {
|
||||
return "", errors.New("col row < 0")
|
||||
}
|
||||
if row > TotalRows {
|
||||
return "", errors.New("row number exceeds maximum limit")
|
||||
}
|
||||
colName, err := ColumnNumberToName(col)
|
||||
return colName + strconv.Itoa(row), err
|
||||
}
|
||||
Reference in New Issue
Block a user