Initial commit

This commit is contained in:
2026-05-20 16:13:58 +08:00
commit ceffbe382e
500 changed files with 157469 additions and 0 deletions
+64
View File
@@ -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
}
+108
View File
@@ -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)
})
}
+84
View File
@@ -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
}
+63
View File
@@ -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
}
}
}
+39
View File
@@ -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)
}
+53
View File
@@ -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
}
+110
View File
@@ -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
}
+40
View File
@@ -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
}
+47
View File
@@ -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)
}
})
}
}
+44
View File
@@ -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()
}
+181
View File
@@ -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)
}
+18
View File
@@ -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)
}
}
+148
View File
@@ -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
}
+7
View File
@@ -0,0 +1,7 @@
package utils
import "github.com/google/uuid"
func UUID() string {
return uuid.New().String()
}
+49
View File
@@ -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
}