Initial commit

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
fish03
2026-09-23 14:43:16 +07:00
co-authored by Claude Opus 5.5
commit bef9f8b4ed
663 changed files with 81305 additions and 0 deletions
+292
View File
@@ -0,0 +1,292 @@
package utils
import (
"math/rand"
"reflect"
)
//数组求交集,差集,并集
/*求并集**/
func Union(slice1, slice2 []string) []string {
m := make(map[string]int)
for _, v := range slice1 {
m[v]++
}
for _, v := range slice2 {
times, _ := m[v]
if times == 0 {
slice1 = append(slice1, v)
}
}
return slice1
}
// 求交集
func Intersect(slice1, slice2 []string) []string {
m := make(map[string]int)
nn := make([]string, 0)
for _, v := range slice1 {
m[v]++
}
for _, v := range slice2 {
times, _ := m[v]
if times == 1 {
nn = append(nn, v)
}
}
return nn
}
// 求交集
func IntersectInt64(slice1, slice2 []int64) []int64 {
m := make(map[int64]int)
nn := make([]int64, 0)
for _, v := range slice1 {
m[v]++
}
for _, v := range slice2 {
times, _ := m[v]
if times == 1 {
nn = append(nn, v)
}
}
return nn
}
// 求差集 slice1-并集
func Difference(slice1, slice2 []string) []string {
m := make(map[string]int)
nn := make([]string, 0)
inter := Intersect(slice1, slice2)
for _, v := range inter {
m[v]++
}
for _, value := range slice1 {
times, _ := m[value]
if times == 0 {
nn = append(nn, value)
}
}
return nn
}
// 求差集 slice1-并集
func DifferenceInt64(slice1, slice2 []int64) []int64 {
m := make(map[int64]int)
nn := make([]int64, 0)
inter := IntersectInt64(slice1, slice2)
for _, v := range inter {
m[v]++
}
for _, value := range slice1 {
times, _ := m[value]
if times == 0 {
nn = append(nn, value)
}
}
return nn
}
// 去重
func RemoveRepeat(slc []int64) []int64 {
result := []int64{} // 存放结果
for i := range slc {
flag := true
for j := range result {
if slc[i] == result[j] {
flag = false // 存在重复元素,标识为false
break
}
}
if flag { // 标识为false,不添加进结果
result = append(result, slc[i])
}
}
return result
}
func IncludeInt32(slc []int32, n int32) bool {
for _, v := range slc {
if v == n {
return true
}
}
return false
}
func IncludeInt64(slc []int64, n int64) bool {
for _, v := range slc {
if v == n {
return true
}
}
return false
}
func RemoveInt64(slc []int64, n int64) []int64 {
for i := 0; i < len(slc); {
if slc[i] == n {
slc = append(slc[:i], slc[i+1:]...)
} else {
i++
}
}
return slc
}
func RemoveString(slc []string, n string) []string {
for i := 0; i < len(slc); {
if slc[i] == n {
slc = append(slc[:i], slc[i+1:]...)
} else {
i++
}
}
return slc
}
// RemoveRepeatString 去重string
func RemoveRepeatString(slc []string) []string {
result := []string{} // 存放结果
for i := range slc {
flag := true
for j := range result {
if slc[i] == result[j] {
flag = false // 存在重复元素,标识为false
break
}
}
if flag { // 标识为false,不添加进结果
result = append(result, slc[i])
}
}
return result
}
func IncludeString(slc []string, s string) bool {
for _, v := range slc {
if v == s {
return true
}
}
return false
}
func EqualInt64s(slice1, slice2 []int64) bool {
if len(slice1) != len(slice2) {
return false
}
for _, v1 := range slice1 {
find := false
for _, v2 := range slice2 {
if v1 == v2 {
find = true
break
}
}
if !find {
return false
}
}
return true
}
func EqualInt64WithOrder(slice1, slice2 []int64) bool {
if len(slice1) != len(slice2) {
return false
}
for k, v1 := range slice1 {
if slice2[k] != v1 {
return false
}
}
return true
}
func EqualString(slice1, slice2 []string) bool {
if len(slice1) != len(slice2) {
return false
}
for _, v1 := range slice1 {
find := false
for _, v2 := range slice2 {
if v1 == v2 {
find = true
break
}
}
if find == false {
return false
}
}
return true
}
// 切片乱序
func ShuffleSlice(slice interface{}) {
rv := reflect.ValueOf(slice)
if rv.Type().Kind() != reflect.Slice {
return
}
length := rv.Len()
if length < 2 {
return
}
swap := reflect.Swapper(slice)
//rand.Seed(time.Now().Unix())
for i := length - 1; i >= 0; i-- {
j := rand.Intn(length)
swap(i, j)
}
return
}
func RemoveOneInt64(slc []int64, n int64) []int64 {
for i := 0; i < len(slc); {
if slc[i] == n {
slc = append(slc[:i], slc[i+1:]...)
break
} else {
i++
}
}
return slc
}
// 求最大值
func SliceMaxInt64(slc []int64) int64 {
maxVal := slc[0]
for i := 1; i < len(slc); i++ {
//从第二个 元素开始循环比较,如果发现有更大的,则交换
if maxVal < slc[i] {
maxVal = slc[i]
}
}
return maxVal
}
// 求最大值
func SliceMinInt64(slc []int64) int64 {
minVal := slc[0]
for i := 1; i < len(slc); i++ {
//从第二个 元素开始循环比较,如果发现有更小的,则交换
if minVal > slc[i] {
minVal = slc[i]
}
}
return minVal
}
func ReverseAny(s interface{}) {
n := reflect.ValueOf(s).Len()
swap := reflect.Swapper(s)
for i, j := 0, n-1; i < j; i, j = i+1, j-1 {
swap(i, j)
}
}
+184
View File
@@ -0,0 +1,184 @@
package utils
import (
"encoding/json"
"path/filepath"
"strings"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type ID = primitive.ObjectID
// ToJsonM 将struct转换为 Json Map
func ToJsonM(obj interface{}) (map[string]interface{}, error) {
j, err := json.Marshal(obj)
if err != nil {
return nil, err
}
m := make(map[string]interface{})
if err := json.Unmarshal(j, &m); err != nil {
return nil, err
}
return m, err
}
// 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
}
func MapToJsonString(m map[string]string) (string, error) {
if data, err := json.Marshal(m); err != nil {
return "", err
} else {
return string(data), nil
}
}
func StructToJsonString(s interface{}) (string, error) {
j, err := json.Marshal(s)
if err != nil {
return "", err
}
m := make(map[string]interface{})
if err := json.Unmarshal(j, &m); err != nil {
return "", err
}
if data, err := json.Marshal(m); err != nil {
return "", err
} else {
return string(data), nil
}
}
// BMapToStruct s is struct ptr
func BMapToStruct(s interface{}, m map[string]interface{}) error {
bsonBytes, err := bson.Marshal(m)
if err != nil {
return err
}
return bson.Unmarshal(bsonBytes, s)
}
// JMapToStruct s is struct ptr
func JMapToStruct(s interface{}, m map[string]interface{}) error {
bsonBytes, err := json.Marshal(m)
if err != nil {
return err
}
return json.Unmarshal(bsonBytes, s)
}
// 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
}
func IDArray(ids []string) ([]ID, error) {
var idArray []ID
for _, id := range ids {
_id, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
idArray = append(idArray, _id)
}
return idArray, nil
}
// 拼接https://
func BindHttpSch(path string) string {
if strings.HasPrefix(path, "http") {
return path
} else {
return "https://" + path
}
}
func BindUrl(hostname string, path ...string) string {
if hostname == "" {
return strings.TrimLeft(filepath.Join(path...), "/")
}
if len(path) == 0 {
return hostname
}
su := strings.TrimLeft(filepath.Join(path...), "/")
pr := strings.TrimRight(hostname, "/")
return pr + "/" + su
}
func ObjectIDs2String(obj []primitive.ObjectID) []string {
arr := []string{}
if len(obj) == 0 {
return arr
}
for _, o := range obj {
arr = append(arr, o.Hex())
}
return arr
}
func String2ObjectID(obj []string) []primitive.ObjectID {
arr := []primitive.ObjectID{}
for _, o := range obj {
oid, err := primitive.ObjectIDFromHex(o)
if err != nil {
continue
}
arr = append(arr, oid)
}
return arr
}
// PercentOf - calculate what percent [number1] is of [number2].
// ex. 300 is 12.5% of 2400
func PercentOf(part int, total int) float64 {
return (float64(part) * float64(100)) / float64(total)
}
func MergeMap(src, dest map[string]interface{}) map[string]interface{} {
if len(src) == 0 {
return dest
}
if len(dest) == 0 {
return src
}
if len(src) == 0 && len(dest) == 0 {
return make(map[string]interface{})
}
for k, v := range dest {
src[k] = v
}
return src
}
+130
View File
@@ -0,0 +1,130 @@
package utils
import (
"bytes"
"encoding/gob"
"reflect"
"time"
)
// DeepCopy 深copy
func DeepCopy(dst, src interface{}) error {
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(src); err != nil {
return err
}
return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
}
// Interface for delegating copy process to type
type Interface interface {
DeepCopy() interface{}
}
// Iface is an alias to Copy; this exists for backwards compatibility reasons.
func Iface(iface interface{}) interface{} {
return Copy(iface)
}
// Copy creates a deep copy of whatever is passed to it and returns the copy
// in an interface{}. The returned value will need to be asserted to the
// correct type.
func Copy(src interface{}) interface{} {
if src == nil {
return nil
}
// Make the interface a reflect.Value
original := reflect.ValueOf(src)
// Make a copy of the same type as the original.
cpy := reflect.New(original.Type()).Elem()
// Recursively copy the original.
copyRecursive(original, cpy)
// Return the copy as an interface.
return cpy.Interface()
}
// copyRecursive does the actual copying of the interface. It currently has
// limited support for what it can handle. Add as needed.
func copyRecursive(original, cpy reflect.Value) {
// check for implement deepcopy.Interface
if original.CanInterface() {
if copier, ok := original.Interface().(Interface); ok {
cpy.Set(reflect.ValueOf(copier.DeepCopy()))
return
}
}
// handle according to original's Kind
switch original.Kind() {
case reflect.Ptr:
// Get the actual value being pointed to.
originalValue := original.Elem()
// if it isn't valid, return.
if !originalValue.IsValid() {
return
}
cpy.Set(reflect.New(originalValue.Type()))
copyRecursive(originalValue, cpy.Elem())
case reflect.Interface:
// If this is a nil, don't do anything
if original.IsNil() {
return
}
// Get the value for the interface, not the pointer.
originalValue := original.Elem()
// Get the value by calling Elem().
copyValue := reflect.New(originalValue.Type()).Elem()
copyRecursive(originalValue, copyValue)
cpy.Set(copyValue)
case reflect.Struct:
t, ok := original.Interface().(time.Time)
if ok {
cpy.Set(reflect.ValueOf(t))
return
}
// Go through each field of the struct and copy it.
for i := 0; i < original.NumField(); i++ {
// The Type's StructField for a given field is checked to see if StructField.PkgPath
// is set to determine if the field is exported or not because CanSet() returns false
// for settable fields. I'm not sure why. -mohae
if original.Type().Field(i).PkgPath != "" {
continue
}
copyRecursive(original.Field(i), cpy.Field(i))
}
case reflect.Slice:
if original.IsNil() {
return
}
// Make a new slice and copy each element.
cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
for i := 0; i < original.Len(); i++ {
copyRecursive(original.Index(i), cpy.Index(i))
}
case reflect.Map:
if original.IsNil() {
return
}
cpy.Set(reflect.MakeMap(original.Type()))
for _, key := range original.MapKeys() {
originalValue := original.MapIndex(key)
copyValue := reflect.New(originalValue.Type()).Elem()
copyRecursive(originalValue, copyValue)
copyKey := Copy(key.Interface())
cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue)
}
default:
cpy.Set(original)
}
}
+56
View File
@@ -0,0 +1,56 @@
package utils
import "os"
// CreateEmptyFile 创建一个固定size大小的空文件
// 如果seek文件失败,则删除文件,并返回错误信息
func CreateEmptyFile(name string, size int64) (f *os.File, err error) {
defer func() {
if err != nil && f != nil {
f.Close()
os.Remove(name)
}
}()
f, err = os.OpenFile(name, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return
}
if _, err = f.Seek(size-1, 0); err != nil {
return
}
_, err = f.Write([]byte{0})
return
}
func WriteFile(data []byte, filename string) (err error) {
defer func() {
if err != nil {
os.Remove(filename)
}
}()
pi := PathForInfo(filename)
if pi.Dir != "" {
if err = os.MkdirAll(pi.Dir, 0755); err != nil {
return
}
}
f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return
}
_, err = f.Write(data)
f.Close()
return
}
func PathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
+12
View File
@@ -0,0 +1,12 @@
package utils
// // MakeCrc
// func MakeCrc(s string) uint32 {
// return crc32.ChecksumIEEE([]byte(s))
// }
// func Md5V(str string) string {
// h := md5.New()
// h.Write([]byte(str))
// return hex.EncodeToString(h.Sum(nil))
// }
+283
View File
@@ -0,0 +1,283 @@
package utils
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strings"
"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: 60 * time.Second, //限制读取response header的时间,默认 timeout + 5*time.Second
ExpectContinueTimeout: 1 * time.Second, //限制client在发送包含 Expect: 100-continue的header到收到继续发送body的response之间的时间等待。
MaxIdleConns: 100, //所有host的连接池最大连接数量,默认无穷大
MaxIdleConnsPerHost: 300, //每个host的连接池最大空闲连接数,默认2
MaxConnsPerHost: 100, //每个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 HttpDelete(url string, header http.Header, body io.Reader, onResponse OnHttpResponse) error {
return HttpMethod(url, http.MethodDelete, 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)
})
}
func POSTJson(url string, headers map[string]string, data interface{}) (*http.Response, error) {
header := make(http.Header)
for k, v := range headers {
header.Set(k, v)
}
body, err := getBytes(data)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", url, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header = header
client := GetHttpClient()
return client.Do(req)
}
func GetWithParam(url string, headers map[string]string, params ...interface{}) (*http.Response, error) {
header := make(http.Header)
for k, v := range headers {
header.Set(k, v)
}
for _, p := range params {
url = addParams(url, toUrlValues(p))
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header = header
client := GetHttpClient()
return client.Do(req)
}
func GetWithJResp(bind interface{}, url string, headers map[string]string, params ...interface{}) (int, error) {
header := make(http.Header)
for k, v := range headers {
header.Set(k, v)
}
for _, p := range params {
url = addParams(url, toUrlValues(p))
}
code := http.StatusBadRequest
err := HttpGet(url, header, nil, func(res *http.Response) error {
code = res.StatusCode
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, bind)
})
return code, err
}
func POSTWithJResp(bind interface{}, url string, headers map[string]string, params interface{}) (int, error) {
header := make(http.Header)
for k, v := range headers {
header.Set(k, v)
}
body, err := getBytes(params)
if err != nil {
return http.StatusBadRequest, err
}
code := http.StatusBadRequest
err = HttpPost(url, header, bytes.NewReader(body), func(res *http.Response) error {
code = res.StatusCode
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, bind)
})
return code, err
}
func POSTJsonWithJResp(bind interface{}, url string, headers map[string]string, data interface{}) (int, error) {
header := make(http.Header)
for k, v := range headers {
header.Set(k, v)
}
header.Set("Content-Type", "application/json")
body, err := getBytes(data)
if err != nil {
return http.StatusBadRequest, err
}
code := http.StatusBadRequest
err = HttpPost(url, header, bytes.NewReader(body), func(res *http.Response) error {
code = res.StatusCode
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, bind)
})
return code, err
}
func DeleteWithJResp(bind interface{}, url string, headers map[string]string, params interface{}) (int, error) {
header := make(http.Header)
for k, v := range headers {
header.Set(k, v)
}
body, err := getBytes(params)
if err != nil {
return http.StatusBadRequest, err
}
code := http.StatusBadRequest
err = HttpDelete(url, header, bytes.NewReader(body), func(res *http.Response) error {
code = res.StatusCode
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, bind)
})
return code, err
}
// Add params to a url string.
func addParams(url_ string, params url.Values) string {
if len(params) == 0 {
return url_
}
if !strings.Contains(url_, "?") {
url_ += "?"
}
if strings.HasSuffix(url_, "?") || strings.HasSuffix(url_, "&") {
url_ += params.Encode()
} else {
url_ += "&" + params.Encode()
}
return url_
}
func toUrlValues(v interface{}) url.Values {
switch t := v.(type) {
case url.Values:
return t
case map[string][]string:
return url.Values(t)
case map[string]string:
rst := make(url.Values)
for k, v := range t {
rst.Add(k, v)
}
return rst
case nil:
return make(url.Values)
default:
panic("Invalid value")
}
}
+127
View File
@@ -0,0 +1,127 @@
package utils
import (
"errors"
"strings"
"time"
"github.com/dgrijalva/jwt-go"
)
const (
userIDKey = "userId"
ipKey = "ip"
uaKey = "ua"
)
type IPClaims struct {
Ip string
Expire time.Duration
UA string
}
func GenerateIpToken(claims IPClaims, secret string) (string, error) {
return generateToken(jwt.MapClaims{
ipKey: claims.Ip,
uaKey: claims.UA,
"exp": Now().Add(claims.Expire).Unix(),
"issuer": "com.butterfly",
"sub": "asign",
"aud": "public",
}, []byte(secret))
}
func ParseIpToken(tokenString string, secret string) (pass bool, ip, ua string, err error) {
t, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
var isExpire *bool
if vErr, ok := err.(*jwt.ValidationError); ok {
e := vErr.Errors&jwt.ValidationErrorExpired > 0
isExpire = &e
}
if err != nil || isExpire == nil {
return
}
var ok bool
var jc jwt.MapClaims
jc, ok = t.Claims.(jwt.MapClaims)
if !ok {
err = errors.New("parse token fail")
return
}
ip, ok = jc[ipKey].(string)
if !ok {
err = errors.New("parse token fail")
return
}
ua, ok = jc[uaKey].(string)
if !ok {
err = errors.New("parse token fail")
return
}
pass = true
return
}
type UserClaims struct {
UserID int64
Expire time.Duration
}
func generateToken(claims jwt.MapClaims, secret []byte) (string, error) {
return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(secret)
}
func GenerateUserToken(claims UserClaims, secret string) (string, error) {
return generateToken(jwt.MapClaims{
userIDKey: claims.UserID,
"exp": Now().Add(claims.Expire).Unix(),
"issuer": "com.butterfly",
"sub": "asign",
"aud": "public",
}, []byte(secret))
}
// ParseToken 解析Token
func ParseUserToken(token string, secret string) (userId int64, newToken string, err error) {
t, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
var isExpire *bool
if vErr, ok := err.(jwt.ValidationError); ok {
e := vErr.Errors&jwt.ValidationErrorExpired > 0
isExpire = &e
}
if err != nil && isExpire == nil {
return
}
jc, ok := t.Claims.(jwt.MapClaims)
if !ok {
err = errors.New("unknown token error1")
return
}
id, ok := jc[userIDKey].(float64)
if !ok {
err = errors.New("unknown token error1")
return
}
userId = int64(id)
if isExpire != nil && *isExpire {
newToken, err = GenerateUserToken(UserClaims{UserID: userId, Expire: 30 * 24 * time.Hour}, secret)
return
}
err = nil
return
}
// Strips 'Bearer ' prefix from bearer token string
func StripBearerPrefixFromTokenString(tok string) string {
if len(tok) > 6 && strings.ToUpper(tok[0:7]) == "BEARER " {
return tok[7:]
} else {
return tok
}
}
+20
View File
@@ -0,0 +1,20 @@
package utils
import (
"math/rand"
"time"
)
// CountDigits 有几位数字
func CountDigits(i int) (count int) {
for i != 0 {
i /= 10
count++
}
return
}
func RandInt(min int, max int) int {
rand.Seed(time.Now().UnixNano())
return min + rand.Intn(max-min)
}
+19
View File
@@ -0,0 +1,19 @@
package utils
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
}
+72
View File
@@ -0,0 +1,72 @@
package utils
import (
"path/filepath"
"strings"
"github.com/PuerkitoBio/purell"
)
// 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
}
// JoinUrlPath 域名与uri拼接(走 purell 标准化,会剥掉尾部斜杠)。
// 适用于 CDN 文件链接(m3u8 分段、vtt、静态图等)这类"不需要尾部斜杠"的场景。
// 需要保留尾部斜杠的页面 URL(sitemap/rss/indexnow)请使用 JoinUrlPathKeepSlash。
func JoinUrlPath(host string, uri string) (urlpath string) {
urlpath = joinRawUrlPath(host, uri)
if urlpath == "" {
return
}
// 标准化 URL
normalized, err := purell.NormalizeURLString(urlpath, purell.FlagsUsuallySafeGreedy)
if err != nil {
return urlpath
}
return normalized
}
// JoinUrlPathKeepSlash 域名与uri拼接,保留 uri 原始的尾部斜杠,不做任何标准化。
// sitemap/rss/indexnow 的页面 URL 必须与路由注册的 /archives/:id/ 等完全一致,
// 否则爬虫每次访问都会被 gin 的 RedirectTrailingSlash 301 到带 / 的地址,
// 浪费抓取预算并造成重定向链。
func JoinUrlPathKeepSlash(host string, uri string) string {
return joinRawUrlPath(host, uri)
}
// joinRawUrlPath 纯拼接,不做任何标准化。
func joinRawUrlPath(host string, uri string) (urlpath string) {
if uri == "" {
return
}
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
}
+56
View File
@@ -0,0 +1,56 @@
package utils
import (
"bytes"
"time"
"videoweb/pub/common/enum/redisconst"
sErr "videoweb/pub/common/errors"
"videoweb/pub/redis"
)
const (
expireTime = 30 * 24 * time.Hour
)
type TokenRedis struct {
}
func NewTokenRedis() *TokenRedis {
return &TokenRedis{}
}
func (tr *TokenRedis) Save(userId int64, token string) (err error) {
if err = tr.Delete(userId); err != nil {
return
}
err = redis.Set(redisconst.AppUserTokenKey(userId), token, expireTime)
if err != nil {
err = sErr.MakeWithErr(sErr.RedisSet, err)
}
return
}
func (tr *TokenRedis) Exists(userId int64, token string) (has bool, err error) {
tokenbytes, err := tr.Get(userId)
if err != nil {
return
}
has = bytes.Compare(tokenbytes, []byte(token)) == 0
return
}
func (tr *TokenRedis) Get(userId int64) (token []byte, err error) {
token, err = redis.GetBytes(redisconst.AppUserTokenKey(userId))
if err != nil {
err = sErr.MakeWithErr(sErr.RedisGet, err)
}
return
}
func (tr *TokenRedis) Delete(userId int64) (err error) {
err = redis.Del(redisconst.AppUserTokenKey(userId))
if err != nil {
err = sErr.MakeWithErr(sErr.RedisDel, err)
}
return
}
+15
View File
@@ -0,0 +1,15 @@
package utils
import (
"bytes"
"os/exec"
)
func ShellCmd(cmdStr string) (string, error) {
cmd := exec.Command("/bin/bash", "-c", cmdStr)
var out bytes.Buffer
cmd.Stdout = &out
ec := cmd.Run()
return out.String(), ec
}
+116
View File
@@ -0,0 +1,116 @@
package utils
import (
"golang.org/x/exp/constraints"
"sort"
)
func SliceConstruct[T any](fn func() T) T {
return fn()
}
type SliceWalkCallback func(index int) bool // true 继续
func SliceWalk(length int, cb SliceWalkCallback) {
for i := 0; i < length; i++ {
if ok := cb(i); ok {
continue
} else {
break
}
}
}
type SliceIncludeFilter func(index int) bool // 返回 true 继续, false 中断
func SliceInclude(length int, filter SliceIncludeFilter) (int, bool) {
for i := 0; i < length; i++ {
if ok := filter(i); ok {
return i, true
}
}
return -1, false
}
type SliceConstructNFilter[T any] func(index int) (T, bool) // 返回 是否保留,项值
func SliceConstructN[T any](length int, filter SliceConstructNFilter[T]) []T {
ret := make([]T, 0)
for i := 0; i < length; i++ {
if item, keep := filter(i); keep {
ret = append(ret, item)
}
}
return ret
}
// SliceUnique 使用泛型实现切片去重
func SliceUnique[T comparable](s []T) []T {
seen := make(map[T]bool)
j := 0
for _, v := range s {
if _, ok := seen[v]; !ok {
seen[v] = true
s[j] = v
j++
}
}
return s[:j]
}
// SlicesDifference 求两个集合的差集
func SlicesDifference[T comparable](a, b []T) []T {
// 使用 map 存储集合 b 的元素,方便快速判断元素是否存在
seen := make(map[T]struct{}, len(b))
for _, v := range b {
seen[v] = struct{}{}
}
// 遍历集合 a,将不在集合 b 中的元素放入新的切片
diff := make([]T, 0)
for _, v := range a {
if _, ok := seen[v]; !ok {
diff = append(diff, v)
}
}
return diff
}
func RemoveSlicesElement[T comparable](slice []T, element T) []T {
// 使用两个指针,一个遍历,一个用来记录有效元素的位置
j := 0
for i := range slice {
if slice[i] != element {
slice[j] = slice[i]
j++
}
}
// 返回截取后的切片
return slice[:j]
}
func SlicesIntersect[T constraints.Ordered | ~string](slice1, slice2 []T) []T {
dict := make(map[T]int)
ret := make([]T, 0)
for _, v1 := range slice1 {
dict[v1]++
}
for _, v2 := range slice2 {
if _, exists := dict[v2]; exists {
ret = append(ret, v2)
}
}
return ret
}
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
}
+27
View File
@@ -0,0 +1,27 @@
package utils
// BinarySearch BinarySearch
func BinarySearch(s []string, t string, n int) (idx int) {
idx = -1
if n == 0 {
return
}
start, end := 0, n-1
for start <= end {
mid := (start + end) >> 1
if s[mid] < t {
start = mid + 1
} else if s[mid] > t {
end = mid - 1
} else {
idx = mid
break
}
}
return
}
var YesOrNo = map[bool]string{
true: "是",
false: "否",
}
+52
View File
@@ -0,0 +1,52 @@
package utils
import (
"fmt"
"github.com/mitchellh/mapstructure"
"reflect"
)
func Struct2Map(obj interface{}) map[string]interface{} {
t := reflect.TypeOf(obj)
v := reflect.ValueOf(obj)
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = reflect.Indirect(v)
}
var data = make(map[string]interface{})
//func (v Value) NumField() int, 返回v持有的结构体类型值的字段数,如果v的Kind不是Struct会panic
for i := 0; i < t.NumField(); i++ {
n := t.Field(i).Name
vv := v.Field(i).Interface()
data[Capitalize(n)] = vv
}
return data
}
func Map2Struct(obj interface{}, mapInfo map[string]interface{}) interface{} {
err := mapstructure.Decode(mapInfo, obj)
if err != nil {
fmt.Println(err)
}
return obj
}
// Capitalize 字符首字母大写
func Capitalize(str string) string {
var upperStr string
vv := []rune(str) // 后文有介绍
for i := 0; i < len(vv); i++ {
if i == 0 {
if vv[i] >= 65 && vv[i] <= 96 { // 后文有介绍
vv[i] += 32 // string的码表相差32位
upperStr += string(vv[i])
} else {
fmt.Println("Not begins with uppercase letter,")
return str
}
} else {
upperStr += string(vv[i])
}
}
return upperStr
}
+276
View File
@@ -0,0 +1,276 @@
package utils
import (
"fmt"
"time"
"videoweb/pub/pubdef"
)
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 BeginOfToday() time.Time {
t := Now()
return BeginOfTime(t)
}
func BeginOfTime(t time.Time) time.Time {
year, month, day := t.Date()
return time.Date(year, month, day, 0, 0, 0, 0, t.Location())
}
func GetNextMonthTime(t time.Time) time.Time {
year, month, day := t.Date()
return time.Date(year, month+1, day, 0, 0, 0, 0, t.Location())
}
func GetNextQuarterTime(t time.Time) time.Time {
year, month, day := t.Date()
return time.Date(year, month+3, day, 0, 0, 0, 0, t.Location())
}
func GetNextYearTime(t time.Time) time.Time {
year, month, day := t.Date()
return time.Date(year+1, month, day, 0, 0, 0, 0, t.Location())
}
func GetForeverTime() time.Time {
t := time.Now()
year, month, day := t.Date()
return time.Date(year+999, month, day, 0, 0, 0, 0, t.Location())
}
func IsoWeek() string {
year, week := time.Now().ISOWeek()
return fmt.Sprintf("%d%02d", year, week)
}
// Deprecated
func EndOfToday() time.Time {
t := Now()
return EndOfTime(t)
}
func EndOfTime(t time.Time) time.Time {
year, month, day := t.Date()
return time.Date(year, month, day, 23, 59, 59, 0, t.Location())
}
func GetDateString(t time.Time) string {
return t.In(LocBJ).Format(pubdef.DateISO)
}
func GetTodayDate(now time.Time) string {
return GetDateString(now.In(time.Local))
}
func GetYesterdayDate(now time.Time) string {
return GetDateString(now.Local().AddDate(0, 0, -1))
}
func GetDayBeforeYesterdayDate(now time.Time) string {
return GetDateString(now.Local().AddDate(0, 0, -2))
}
func GetDayRange(t time.Time) (dayStart time.Time, dayEnd time.Time) {
dayStart = BeginOfTime(t)
dayEnd = EndOfTime(t)
return
}
// GetDayRangeByDate 返回一天的开始和结束时间, 00:00:00 - 23:59:59
func GetDayRangeByDate(date string) (dayStart time.Time, dayEnd time.Time, err error) {
var d time.Time
d, err = time.ParseInLocation(pubdef.DateISO, date, LocBJ)
if err != nil {
return
}
dayStart, dayEnd = GetDayRange(d)
return
}
func GetDayEnd(date string) time.Time {
d, _ := time.ParseInLocation(pubdef.DateISO, date, LocBJ)
return EndOfTime(d)
}
func GetDayBegin(date string) time.Time {
d, _ := time.ParseInLocation(pubdef.DateISO, date, LocBJ)
return BeginOfTime(d)
}
func BeginWithThisMonth() time.Time {
t := Now()
year, month, _ := t.Date()
return time.Date(year, month, 1, 0, 0, 0, 0, t.Location())
}
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 EndOfLastMonth(t time.Time) time.Time {
return BeginOfMonth(t).Add(-time.Nanosecond)
}
func EndOfLastMonthDate(t time.Time) string {
return GetDateString(EndOfLastMonth(t))
}
func EndOfLastWeek(t time.Time) time.Time {
return BeginOfWeekByTime(t).Add(-time.Nanosecond)
}
func EndOfLastWeekDate(t time.Time) string {
return GetDateString(EndOfLastWeek(t))
}
// Deprecated
func BeginOfWeek() time.Time {
return BeginOfWeekByTime(Now())
}
func WeekDateRange(t time.Time) (string, string) {
b := BeginOfWeekByTime(t)
e := EndOfWeekByTime(t)
return GetDateString(b), GetDateString(e)
}
func MonthDateRange(t time.Time) (string, string) {
b := BeginOfMonth(t)
e := EndOfMonth(t)
return GetDateString(b), GetDateString(e)
}
func BeginOfWeekByTime(t time.Time) time.Time {
bt := BeginOfTime(t)
weekday := int(t.Weekday())
weekStartDayInt := int(time.Monday)
if weekday < weekStartDayInt {
weekday = weekday + 7 - weekStartDayInt
} else {
weekday = weekday - weekStartDayInt
}
return bt.AddDate(0, 0, -weekday)
}
func EndOfWeekByTime(t time.Time) time.Time {
return BeginOfWeekByTime(t).AddDate(0, 0, 7).Add(-time.Nanosecond)
}
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 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()
}
func IsSameYearAndMonth(t1, t2 time.Time) bool {
return t1.Year() == t2.Year() &&
t1.Month() == t2.Month()
}
// 获取月的第一天
func GetFirstDateOfMonth(d time.Time) time.Time {
d = d.AddDate(0, 0, -d.Day()+1)
return GetZeroTime(d)
}
// 获取月的最后一天
func GetLastDateOfMonth(d time.Time) time.Time {
return GetFirstDateOfMonth(d).AddDate(0, 1, -1)
}
// 获取周的星期一(星期一~星期日)
func GetMondayOfWeek(d time.Time) time.Time {
offset := (int(d.Weekday()) + 6) % 7
d = d.AddDate(0, 0, -offset)
return GetZeroTime(d)
}
// 获取周的星期日(星期一~星期日)
func GetSundayOfWeek(d time.Time) time.Time {
offset := 6 - (int(d.Weekday())+6)%7
d = d.AddDate(0, 0, offset)
return GetZeroTime(d)
}
// 获取某一天的0点时间
func GetZeroTime(d time.Time) time.Time {
return time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, d.Location())
}
// 获取当前整点时间
func GetWholeTime(d time.Time) time.Time {
return time.Date(d.Year(), d.Month(), d.Day(), d.Hour(), 0, 0, 0, d.Location())
}
func MustParseTime(s string) time.Time {
layouts := []string{
time.RFC3339,
pubdef.DateISO,
pubdef.TimeISO,
}
for _, layout := range layouts {
t, err := time.ParseInLocation(layout, s, time.Local)
if err != nil {
continue
}
return t
}
panic(fmt.Errorf("cannot parse %s as time.Time", s))
}
// 查看两个时间差了多少天,差一秒都算一天
func TimeSub(t1, t2 time.Time) int64 {
t1 = time.Date(t1.Year(), t1.Month(), t1.Day(), 0, 0, 0, 0, time.Local)
t2 = time.Date(t2.Year(), t2.Month(), t2.Day(), 0, 0, 0, 0, time.Local)
return int64(t1.Sub(t2).Hours() / 24)
}
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())
}
+130
View File
@@ -0,0 +1,130 @@
package utils
import (
"fmt"
"reflect"
"testing"
"time"
)
func Test_GetDayRangeByDate(t *testing.T) {
begin, end, _ := GetDayRangeByDate("2022-04-08")
fmt.Println(begin, end)
}
func Test_GetDayRange(t *testing.T) {
now := time.Now()
fmt.Println(GetDayRange(now))
}
func Test_GetDateString(t *testing.T) {
fmt.Println(GetDateString(time.Now()))
}
func Test_GetTodayDate(t *testing.T) {
fmt.Println(GetTodayDate(time.Now()))
}
func Test_GetYesterdayDate(t *testing.T) {
fmt.Println(GetYesterdayDate(time.Now()))
}
func Test_GetDayBeforeYesterdayDate(t *testing.T) {
fmt.Println(GetDayBeforeYesterdayDate(time.Now()))
}
func Test_BeginOfWeekByTime(t *testing.T) {
{
// 如果当前是周日,获取到的结果应该是这周周一而不是下周周一
now := time.Date(2022, 4, 3, 1, 0, 0, 0, time.Local)
actual := BeginOfWeekByTime(now)
expect := time.Date(2022, 3, 28, 0, 0, 0, 0, time.Local)
if !reflect.DeepEqual(expect, actual) {
t.Fatalf("fail")
}
}
{
now := time.Date(2022, 3, 28, 0, 0, 0, 0, time.Local)
actual := BeginOfWeekByTime(now)
expect := time.Date(2022, 3, 28, 0, 0, 0, 0, time.Local)
if !reflect.DeepEqual(expect, actual) {
t.Fatalf("fail")
}
}
}
func Test_GetDayEnd(t *testing.T) {
fmt.Println(GetDayEnd("2020-04-09"))
}
func Test_GetDayBegin(t *testing.T) {
fmt.Println(GetDayBegin("2020-04-09"))
}
func Test_EndOfWeekByTime(t *testing.T) {
now := time.Now()
fmt.Println(EndOfWeekByTime(now))
}
func Test_EndOfMonth(t *testing.T) {
now := time.Now()
fmt.Println(BeginOfMonth(now))
fmt.Println(EndOfMonth(now))
}
func Test_WeekDateRange(t *testing.T) {
fmt.Println(WeekDateRange(time.Now()))
}
func Test_MonthDateRange(t *testing.T) {
fmt.Println(MonthDateRange(time.Now()))
}
func Test_EndOfLastWeek(t *testing.T) {
fmt.Println(EndOfLastWeek(time.Now()))
}
func Test_EndOfLastMonth(t *testing.T) {
fmt.Println(EndOfLastMonth(time.Now()))
}
func Test_EndOfLastWeekDate(t *testing.T) {
fmt.Println(EndOfLastWeekDate(time.Now()))
}
func Test_EndOfLastMonthDate(t *testing.T) {
fmt.Println(EndOfLastMonthDate(time.Now()))
}
func TestMustParseTime(t *testing.T) {
type args struct {
s string
}
tests := []struct {
name string
args args
want time.Time
}{
{
name: "case1",
args: args{
s: "2022-04-25",
},
want: time.Date(2022, 4, 25, 0, 0, 0, 0, time.Local),
},
{
name: "case2",
args: args{
s: "2022-04-25 11:12:13",
},
want: time.Date(2022, 4, 25, 11, 12, 13, 0, time.Local),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := MustParseTime(tt.args.s); !reflect.DeepEqual(got, tt.want) {
t.Errorf("MustParseTime() = %v, want %v", got, tt.want)
}
})
}
}
+30
View File
@@ -0,0 +1,30 @@
package utils
import (
"strings"
"videoweb/pub/common/enum/consts"
)
func UASplit(useragent string) (phoneModel string, phoneSys string, appVer string) {
if useragent == "" {
return
}
infos := strings.Split(useragent, ";")
if len(infos) != 3 {
return
}
phoneinfos := strings.Split(strings.Trim(infos[1], " "), " ")
if len(phoneinfos) == 2 {
phoneModel = strings.Trim(phoneinfos[0], " ")
phoneSys = strings.Trim(phoneinfos[1], " ")
}
appVer = strings.Trim(infos[2], " ")
return
}
func UADeviceType(ua string) string {
if strings.Contains(ua, "iPhone") {
return consts.IOS
}
return consts.Android
}
+63
View File
@@ -0,0 +1,63 @@
package utils
import (
"fmt"
"math/rand"
"runtime/debug"
"strings"
"time"
"videoweb/pub/common/log"
"golang.org/x/exp/constraints"
)
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 GetRandom(min, max int) int64 {
rand.Seed(time.Now().UnixNano())
return int64(rand.Intn(max-min+1) + min)
}
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
}
// Shuffle 使用 Fisher-Yates 算法对切片进行随机排序
func Shuffle[T any](slice []T) {
rand.Seed(time.Now().UnixNano())
n := len(slice)
for i := n - 1; i > 0; i-- {
j := rand.Intn(i + 1)
slice[i], slice[j] = slice[j], slice[i]
}
}
func GetRandomMacAddress() string {
buf := make([]byte, 6)
rand.Read(buf)
buf[0] |= 2
mac := fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5])
return strings.ToUpper(mac)
}
+10
View File
@@ -0,0 +1,10 @@
package utils
import "regexp"
// mobile verify
func VerifyMobileFormat(mobileNum string) bool {
regular := "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$"
reg := regexp.MustCompile(regular)
return reg.MatchString(mobileNum)
}
+43
View File
@@ -0,0 +1,43 @@
package utils
import (
"strconv"
"strings"
)
func getSubVersions(ver string) []int {
var vers [4]int
vv := strings.Split(ver, ".")
for i, v := range vv {
vers[i], _ = strconv.Atoi(v)
}
return vers[:]
}
// VersionCompare
func VersionCompare(a, b string) int {
aa := getSubVersions(a)
bb := getSubVersions(b)
for i := 0; i < len(aa) && i < len(bb); i++ {
if aa[i] == bb[i] {
continue
}
if aa[i] < bb[i] {
return -1
}
return 1
}
if len(aa) == len(bb) {
return 0
}
if len(aa) < len(bb) {
return -1
}
return 1
}
+19
View File
@@ -0,0 +1,19 @@
package utils
import (
"fmt"
"testing"
)
func TestVersionCompare(t *testing.T) {
fmt.Printf("2.10.9 vs 2.11 = %d\n", VersionCompare("2.10.9", "2.11"))
fmt.Printf("2.11.9 vs 2.11 = %d\n", VersionCompare("2.11.9", "2.11"))
fmt.Printf("1.10.9 vs 2.11 = %d\n", VersionCompare("1.10.9", "2.11"))
fmt.Printf("2.10.9 vs 2.10.1 = %d\n", VersionCompare("2.10.9", "2.11.1"))
fmt.Printf("2.10.9 vs 2.10.10 = %d\n", VersionCompare("2.10.9", "2.10.10"))
fmt.Printf("2.10.9 vs 2.10.9.0 = %d\n", VersionCompare("2.10.9", "2.10.9.0"))
fmt.Printf("1 vs 1.0.0.0 = %d\n", VersionCompare("2.10.9", "2.10.9.0"))
fmt.Printf("2.10.9 vs 2.10.9.-1 = %d\n", VersionCompare("2.10.9", "2.10.9.-1"))
}