mirror of
https://github.com/openimsdk/open-im-server.git
synced 2026-05-09 03:25:59 +08:00
feat: use robot to migrate code
Signed-off-by: kubbot & kubecub <3293172751ysy@gmail.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func GetContent(msg *sdkws.MsgData) string {
|
||||
if msg.ContentType >= constant.NotificationBegin && msg.ContentType <= constant.NotificationEnd {
|
||||
var tips sdkws.TipsComm
|
||||
_ = proto.Unmarshal(msg.Content, &tips)
|
||||
content := tips.JsonDetail
|
||||
return content
|
||||
} else {
|
||||
return string(msg.Content)
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func CorsHandler() gin.HandlerFunc {
|
||||
return func(context *gin.Context) {
|
||||
context.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
context.Header("Access-Control-Allow-Methods", "*")
|
||||
context.Header("Access-Control-Allow-Headers", "*")
|
||||
context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
|
||||
context.Header("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒
|
||||
context.Header("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息 默认设置为true
|
||||
context.Header("content-type", "application/json") // 设置返回格式是json
|
||||
//Release all option pre-requests
|
||||
if context.Request.Method == http.MethodOptions {
|
||||
context.JSON(http.StatusOK, "Options Request!")
|
||||
}
|
||||
context.Next()
|
||||
}
|
||||
}
|
||||
+45
-1
@@ -1,14 +1,26 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"Open_IM/pkg/common/constant"
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
BYTE = 1 << (10 * iota)
|
||||
KILOBYTE
|
||||
MEGABYTE
|
||||
GIGABYTE
|
||||
TERABYTE
|
||||
PETABYTE
|
||||
EXABYTE
|
||||
)
|
||||
|
||||
// Determine whether the given path is a folder
|
||||
func IsDir(path string) bool {
|
||||
s, err := os.Stat(path)
|
||||
@@ -37,3 +49,35 @@ func GetNewFileNameAndContentType(fileName string, fileType int) (string, string
|
||||
}
|
||||
return newName, contentType
|
||||
}
|
||||
|
||||
func ByteSize(bytes uint64) string {
|
||||
unit := ""
|
||||
value := float64(bytes)
|
||||
switch {
|
||||
case bytes >= EXABYTE:
|
||||
unit = "E"
|
||||
value = value / EXABYTE
|
||||
case bytes >= PETABYTE:
|
||||
unit = "P"
|
||||
value = value / PETABYTE
|
||||
case bytes >= TERABYTE:
|
||||
unit = "T"
|
||||
value = value / TERABYTE
|
||||
case bytes >= GIGABYTE:
|
||||
unit = "G"
|
||||
value = value / GIGABYTE
|
||||
case bytes >= MEGABYTE:
|
||||
unit = "M"
|
||||
value = value / MEGABYTE
|
||||
case bytes >= KILOBYTE:
|
||||
unit = "K"
|
||||
value = value / KILOBYTE
|
||||
case bytes >= BYTE:
|
||||
unit = "B"
|
||||
case bytes == 0:
|
||||
return "0"
|
||||
}
|
||||
result := strconv.FormatFloat(value, 'f', 1, 64)
|
||||
result = strings.TrimSuffix(result, ".0")
|
||||
return result + unit
|
||||
}
|
||||
|
||||
+13
-14
@@ -1,26 +1,25 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"Open_IM/pkg/common/config"
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
var ServerIP = ""
|
||||
|
||||
func init() {
|
||||
//fixme In the configuration file, ip takes precedence, if not, get the valid network card ip of the machine
|
||||
if config.Config.ServerIP != "" {
|
||||
ServerIP = config.Config.ServerIP
|
||||
return
|
||||
}
|
||||
|
||||
// see https://gist.github.com/jniltinho/9787946#gistcomment-3019898
|
||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||
func GetLocalIP() (string, error) {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
return "", err
|
||||
}
|
||||
for _, address := range addrs {
|
||||
|
||||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
return ipnet.IP.String(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
ServerIP = localAddr.IP.String()
|
||||
return "", errors.New("no ip")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/bwmarrin/snowflake"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
idGenerator, err = snowflake.NewNode(getNodeNum())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getNodeNum() int64 {
|
||||
return 1
|
||||
}
|
||||
|
||||
var idGenerator *snowflake.Node
|
||||
|
||||
func GenID() string {
|
||||
return idGenerator.Generate().String()
|
||||
}
|
||||
|
||||
func GenIDs(count int) []string {
|
||||
//impl
|
||||
return []string{}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package utils
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGenID(t *testing.T) {
|
||||
m := map[string]struct{}{}
|
||||
for i := 0; i < 2000; i++ {
|
||||
got := GenID()
|
||||
if _, ok := m[got]; !ok {
|
||||
m[got] = struct{}{}
|
||||
} else {
|
||||
t.Error("id generate error", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -2,14 +2,15 @@ package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/nfnt/resize"
|
||||
"golang.org/x/image/bmp"
|
||||
"image"
|
||||
"image/gif"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/nfnt/resize"
|
||||
"golang.org/x/image/bmp"
|
||||
)
|
||||
|
||||
func GenSmallImage(src, dst string) error {
|
||||
@@ -51,6 +52,4 @@ func scale(in io.Reader, out io.Writer, width, height, quality int) error {
|
||||
default:
|
||||
return errors.New("ERROR FORMAT")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
_, b, _, _ = runtime.Caller(0)
|
||||
// Root folder of this project
|
||||
Root = filepath.Join(filepath.Dir(b), "../..")
|
||||
)
|
||||
|
||||
func Test_GenSmallImage(t *testing.T) {
|
||||
println(Root)
|
||||
err := GenSmallImage(Root+"/docs/open-im-logo.png", Root+"/out-test/open-im-logo-test.png")
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = GenSmallImage(Root+"/docs/open-im-logo.png", "out-test/open-im-logo-test.png")
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = GenSmallImage(Root+"/docs/Architecture.jpg", "out-test/Architecture-test.jpg")
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
@@ -124,6 +124,7 @@ func GetSwitchFromOptions(Options map[string]bool, key string) (result bool) {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func SetSwitchFromOptions(options map[string]bool, key string, value bool) {
|
||||
if options == nil {
|
||||
options = make(map[string]bool, 5)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_Md5(t *testing.T) {
|
||||
result := Md5("go")
|
||||
assert.Equal(t, result, "34d1f91fb2e514b8576fab1a75a89a6b")
|
||||
|
||||
result2 := Md5("go")
|
||||
assert.Equal(t, result, result2)
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package utils
|
||||
|
||||
import "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
|
||||
type Options map[string]bool
|
||||
type OptionsOpt func(Options)
|
||||
|
||||
func NewOptions(opts ...OptionsOpt) Options {
|
||||
options := make(map[string]bool, 11)
|
||||
options[constant.IsNotNotification] = false
|
||||
options[constant.IsSendMsg] = false
|
||||
options[constant.IsHistory] = false
|
||||
options[constant.IsPersistent] = false
|
||||
options[constant.IsOfflinePush] = false
|
||||
options[constant.IsUnreadCount] = false
|
||||
options[constant.IsConversationUpdate] = false
|
||||
options[constant.IsSenderSync] = false
|
||||
options[constant.IsNotPrivate] = false
|
||||
options[constant.IsSenderConversationUpdate] = false
|
||||
options[constant.IsSenderNotificationPush] = false
|
||||
options[constant.IsReactionFromCache] = false
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func NewMsgOptions() Options {
|
||||
options := make(map[string]bool, 11)
|
||||
options[constant.IsOfflinePush] = false
|
||||
return make(map[string]bool)
|
||||
}
|
||||
|
||||
func WithOptions(options Options, opts ...OptionsOpt) Options {
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func WithNotNotification(b bool) OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsNotNotification] = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithSendMsg(b bool) OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsSendMsg] = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithHistory(b bool) OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsHistory] = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithPersistent() OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsPersistent] = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithOfflinePush(b bool) OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsOfflinePush] = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithUnreadCount(b bool) OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsUnreadCount] = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithConversationUpdate() OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsConversationUpdate] = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithSenderSync() OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsSenderSync] = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithNotPrivate() OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsNotPrivate] = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithSenderConversationUpdate() OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsSenderConversationUpdate] = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithSenderNotificationPush() OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsSenderNotificationPush] = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithReactionFromCache() OptionsOpt {
|
||||
return func(options Options) {
|
||||
options[constant.IsReactionFromCache] = true
|
||||
}
|
||||
}
|
||||
|
||||
func (o Options) Is(notification string) bool {
|
||||
v, ok := o[notification]
|
||||
if !ok || v {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (o Options) IsNotNotification() bool {
|
||||
return o.Is(constant.IsNotNotification)
|
||||
}
|
||||
|
||||
func (o Options) IsSendMsg() bool {
|
||||
return o.Is(constant.IsSendMsg)
|
||||
}
|
||||
|
||||
func (o Options) IsHistory() bool {
|
||||
return o.Is(constant.IsHistory)
|
||||
}
|
||||
|
||||
func (o Options) IsPersistent() bool {
|
||||
return o.Is(constant.IsPersistent)
|
||||
}
|
||||
|
||||
func (o Options) IsOfflinePush() bool {
|
||||
return o.Is(constant.IsOfflinePush)
|
||||
}
|
||||
|
||||
func (o Options) IsUnreadCount() bool {
|
||||
return o.Is(constant.IsUnreadCount)
|
||||
}
|
||||
|
||||
func (o Options) IsConversationUpdate() bool {
|
||||
return o.Is(constant.IsConversationUpdate)
|
||||
}
|
||||
|
||||
func (o Options) IsSenderSync() bool {
|
||||
return o.Is(constant.IsSenderSync)
|
||||
}
|
||||
|
||||
func (o Options) IsNotPrivate() bool {
|
||||
return o.Is(constant.IsNotPrivate)
|
||||
}
|
||||
|
||||
func (o Options) IsSenderConversationUpdate() bool {
|
||||
return o.Is(constant.IsSenderConversationUpdate)
|
||||
}
|
||||
|
||||
func (o Options) IsSenderNotificationPush() bool {
|
||||
return o.Is(constant.IsSenderNotificationPush)
|
||||
}
|
||||
|
||||
func (o Options) IsReactionFromCache() bool {
|
||||
return o.Is(constant.IsReactionFromCache)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package utils
|
||||
|
||||
import "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
|
||||
func GetPage(pagination *sdkws.RequestPagination) (pageNumber, showNumber int32) {
|
||||
if pagination != nil {
|
||||
return pagination.PageNumber, pagination.ShowNumber
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_PlatformIDToName(t *testing.T) {
|
||||
assert.Equal(t, constant.PlatformIDToName(1), "IOS")
|
||||
assert.Equal(t, constant.PlatformIDToName(2), "Android")
|
||||
assert.Equal(t, constant.PlatformIDToName(3), "Windows")
|
||||
assert.Equal(t, constant.PlatformIDToName(4), "OSX")
|
||||
assert.Equal(t, constant.PlatformIDToName(5), "Web")
|
||||
assert.Equal(t, constant.PlatformIDToName(6), "MiniWeb")
|
||||
assert.Equal(t, constant.PlatformIDToName(7), "Linux")
|
||||
|
||||
assert.Equal(t, constant.PlatformIDToName(0), "")
|
||||
}
|
||||
|
||||
func Test_PlatformNameToID(t *testing.T) {
|
||||
assert.Equal(t, constant.PlatformNameToID("IOS"), int32(1))
|
||||
assert.Equal(t, constant.PlatformNameToID("Android"), int32(2))
|
||||
assert.Equal(t, constant.PlatformNameToID("Windows"), int32(3))
|
||||
assert.Equal(t, constant.PlatformNameToID("OSX"), int32(4))
|
||||
assert.Equal(t, constant.PlatformNameToID("Web"), int32(5))
|
||||
assert.Equal(t, constant.PlatformNameToID("MiniWeb"), int32(6))
|
||||
assert.Equal(t, constant.PlatformNameToID("Linux"), int32(7))
|
||||
|
||||
assert.Equal(t, constant.PlatformNameToID("UnknownDevice"), int32(0))
|
||||
assert.Equal(t, constant.PlatformNameToID(""), int32(0))
|
||||
}
|
||||
|
||||
func Test_PlatformNameToClass(t *testing.T) {
|
||||
assert.Equal(t, constant.PlatformNameToClass("IOS"), "Mobile")
|
||||
assert.Equal(t, constant.PlatformNameToClass("Android"), "Mobile")
|
||||
assert.Equal(t, constant.PlatformNameToClass("OSX"), "PC")
|
||||
assert.Equal(t, constant.PlatformNameToClass("Windows"), "PC")
|
||||
assert.Equal(t, constant.PlatformNameToClass("Web"), "PC")
|
||||
assert.Equal(t, constant.PlatformNameToClass("MiniWeb"), "Mobile")
|
||||
assert.Equal(t, constant.PlatformNameToClass("Linux"), "PC")
|
||||
|
||||
assert.Equal(t, constant.PlatformNameToClass("UnknownDevice"), "")
|
||||
assert.Equal(t, constant.PlatformNameToClass(""), "")
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorAbort = errors.New("stop retry")
|
||||
ErrorTimeout = errors.New("retry timeout")
|
||||
ErrorContextDeadlineExceed = errors.New("context deadline exceeded")
|
||||
ErrorEmptyRetryFunc = errors.New("empty retry function")
|
||||
ErrorTimeFormat = errors.New("time out err")
|
||||
)
|
||||
|
||||
type RetriesFunc func() error
|
||||
type Option func(c *Config)
|
||||
type HookFunc func()
|
||||
type RetriesChecker func(err error) (needRetry bool)
|
||||
type Config struct {
|
||||
MaxRetryTimes int
|
||||
Timeout time.Duration
|
||||
RetryChecker RetriesChecker
|
||||
Strategy Strategy
|
||||
RecoverPanic bool
|
||||
BeforeTry HookFunc
|
||||
AfterTry HookFunc
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultMaxRetryTimes = 3
|
||||
DefaultTimeout = time.Minute
|
||||
DefaultInterval = time.Second * 2
|
||||
DefaultRetryChecker = func(err error) bool {
|
||||
return !errors.Is(err, ErrorAbort) // not abort error, should continue retry
|
||||
}
|
||||
)
|
||||
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
MaxRetryTimes: DefaultMaxRetryTimes,
|
||||
RetryChecker: DefaultRetryChecker,
|
||||
Timeout: DefaultTimeout,
|
||||
Strategy: NewLinear(DefaultInterval),
|
||||
BeforeTry: func() {},
|
||||
AfterTry: func() {},
|
||||
}
|
||||
}
|
||||
|
||||
func WithTimeout(timeout time.Duration) Option {
|
||||
return func(c *Config) {
|
||||
c.Timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func WithMaxRetryTimes(times int) Option {
|
||||
return func(c *Config) {
|
||||
c.MaxRetryTimes = times
|
||||
}
|
||||
}
|
||||
|
||||
func WithRecoverPanic() Option {
|
||||
return func(c *Config) {
|
||||
c.RecoverPanic = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithBeforeHook(hook HookFunc) Option {
|
||||
return func(c *Config) {
|
||||
c.BeforeTry = hook
|
||||
}
|
||||
}
|
||||
|
||||
func WithAfterHook(hook HookFunc) Option {
|
||||
return func(c *Config) {
|
||||
c.AfterTry = hook
|
||||
}
|
||||
}
|
||||
|
||||
func WithRetryChecker(checker RetriesChecker) Option {
|
||||
return func(c *Config) {
|
||||
c.RetryChecker = checker
|
||||
}
|
||||
}
|
||||
|
||||
func WithBackOffStrategy(s BackoffStrategy, duration time.Duration) Option {
|
||||
return func(c *Config) {
|
||||
switch s {
|
||||
case StrategyConstant:
|
||||
c.Strategy = NewConstant(duration)
|
||||
case StrategyLinear:
|
||||
c.Strategy = NewLinear(duration)
|
||||
case StrategyFibonacci:
|
||||
c.Strategy = NewFibonacci(duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithCustomStrategy(s Strategy) Option {
|
||||
return func(c *Config) {
|
||||
c.Strategy = s
|
||||
}
|
||||
}
|
||||
|
||||
func Do(ctx context.Context, fn RetriesFunc, opts ...Option) error {
|
||||
if fn == nil {
|
||||
return ErrorEmptyRetryFunc
|
||||
}
|
||||
var (
|
||||
abort = make(chan struct{}, 1) // caller choose to abort retry
|
||||
run = make(chan error, 1)
|
||||
panicInfoChan = make(chan string, 1)
|
||||
|
||||
timer *time.Timer
|
||||
runErr error
|
||||
)
|
||||
config := newDefaultConfig()
|
||||
for _, o := range opts {
|
||||
o(config)
|
||||
}
|
||||
if config.Timeout > 0 {
|
||||
timer = time.NewTimer(config.Timeout)
|
||||
} else {
|
||||
return ErrorTimeFormat
|
||||
}
|
||||
go func() {
|
||||
var err error
|
||||
defer func() {
|
||||
if e := recover(); e == nil {
|
||||
return
|
||||
} else {
|
||||
panicInfoChan <- fmt.Sprintf("retry function panic has occured, err=%v, stack:%s", e, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for i := 0; i < config.MaxRetryTimes; i++ {
|
||||
config.BeforeTry()
|
||||
err = fn()
|
||||
config.AfterTry()
|
||||
if err == nil {
|
||||
run <- nil
|
||||
return
|
||||
}
|
||||
// check whether to retry
|
||||
if config.RetryChecker != nil {
|
||||
needRetry := config.RetryChecker(err)
|
||||
if !needRetry {
|
||||
abort <- struct{}{}
|
||||
return
|
||||
}
|
||||
}
|
||||
if config.Strategy != nil {
|
||||
interval := config.Strategy.Sleep(i + 1)
|
||||
<-time.After(interval)
|
||||
}
|
||||
}
|
||||
run <- err
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// context deadline exceed
|
||||
return ErrorContextDeadlineExceed
|
||||
case <-timer.C:
|
||||
// timeout
|
||||
return ErrorTimeout
|
||||
case <-abort:
|
||||
// caller abort
|
||||
return ErrorAbort
|
||||
case msg := <-panicInfoChan:
|
||||
// panic occurred
|
||||
if !config.RecoverPanic {
|
||||
panic(msg)
|
||||
}
|
||||
runErr = fmt.Errorf("panic occurred=%s", msg)
|
||||
case e := <-run:
|
||||
// normal run
|
||||
if e != nil {
|
||||
runErr = fmt.Errorf("retry failed, err=%w", e)
|
||||
}
|
||||
}
|
||||
return runErr
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package retry
|
||||
|
||||
import "time"
|
||||
|
||||
type BackoffStrategy int
|
||||
|
||||
const (
|
||||
StrategyConstant BackoffStrategy = iota
|
||||
StrategyLinear
|
||||
StrategyFibonacci
|
||||
)
|
||||
|
||||
type Strategy interface {
|
||||
Sleep(times int) time.Duration
|
||||
}
|
||||
type Constant struct {
|
||||
startInterval time.Duration
|
||||
}
|
||||
|
||||
func NewConstant(d time.Duration) *Constant {
|
||||
return &Constant{startInterval: d}
|
||||
}
|
||||
|
||||
type Linear struct {
|
||||
startInterval time.Duration
|
||||
}
|
||||
|
||||
func NewLinear(d time.Duration) *Linear {
|
||||
return &Linear{startInterval: d}
|
||||
}
|
||||
|
||||
type Fibonacci struct {
|
||||
startInterval time.Duration
|
||||
}
|
||||
|
||||
func NewFibonacci(d time.Duration) *Fibonacci {
|
||||
return &Fibonacci{startInterval: d}
|
||||
}
|
||||
|
||||
func (c *Constant) Sleep(_ int) time.Duration {
|
||||
return c.startInterval
|
||||
}
|
||||
func (l *Linear) Sleep(times int) time.Duration {
|
||||
return l.startInterval * time.Duration(times)
|
||||
|
||||
}
|
||||
func (f *Fibonacci) Sleep(times int) time.Duration {
|
||||
return f.startInterval * time.Duration(fibonacciNumber(times))
|
||||
|
||||
}
|
||||
func fibonacciNumber(n int) int {
|
||||
if n == 0 || n == 1 {
|
||||
return n
|
||||
}
|
||||
return fibonacciNumber(n-1) + fibonacciNumber(n-2)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package splitter
|
||||
|
||||
type SplitResult struct {
|
||||
Item []string
|
||||
}
|
||||
type Splitter struct {
|
||||
splitCount int
|
||||
data []string
|
||||
}
|
||||
|
||||
func NewSplitter(splitCount int, data []string) *Splitter {
|
||||
return &Splitter{splitCount: splitCount, data: data}
|
||||
}
|
||||
func (s *Splitter) GetSplitResult() (result []*SplitResult) {
|
||||
remain := len(s.data) % s.splitCount
|
||||
integer := len(s.data) / s.splitCount
|
||||
for i := 0; i < integer; i++ {
|
||||
r := new(SplitResult)
|
||||
r.Item = s.data[i*s.splitCount : (i+1)*s.splitCount]
|
||||
result = append(result, r)
|
||||
}
|
||||
if remain > 0 {
|
||||
r := new(SplitResult)
|
||||
r.Item = s.data[integer*s.splitCount:]
|
||||
result = append(result, r)
|
||||
}
|
||||
return result
|
||||
}
|
||||
+50
-15
@@ -7,7 +7,6 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"Open_IM/pkg/common/constant"
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
@@ -33,7 +32,11 @@ func Int32ToString(i int32) string {
|
||||
return strconv.FormatInt(int64(i), 10)
|
||||
}
|
||||
|
||||
//judge a string whether in the string list
|
||||
func Uint32ToString(i uint32) string {
|
||||
return strconv.FormatInt(int64(i), 10)
|
||||
}
|
||||
|
||||
// judge a string whether in the string list
|
||||
func IsContain(target string, List []string) bool {
|
||||
for _, element := range List {
|
||||
|
||||
@@ -51,7 +54,14 @@ func IsContainInt32(target int32, List []int32) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsContainInt(target int, List []int) bool {
|
||||
for _, element := range List {
|
||||
if target == element {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
func InterfaceArrayToStringArray(data []interface{}) (i []string) {
|
||||
for _, param := range data {
|
||||
i = append(i, param.(string))
|
||||
@@ -69,7 +79,7 @@ func StructToJsonBytes(param interface{}) []byte {
|
||||
return dataType
|
||||
}
|
||||
|
||||
//The incoming parameter must be a pointer
|
||||
// The incoming parameter must be a pointer
|
||||
func JsonStringToStruct(s string, args interface{}) error {
|
||||
err := json.Unmarshal([]byte(s), args)
|
||||
return err
|
||||
@@ -79,20 +89,45 @@ func GetMsgID(sendID string) string {
|
||||
t := int64ToString(GetCurrentTimestampByNano())
|
||||
return Md5(t + sendID + int64ToString(rand.Int63n(GetCurrentTimestampByNano())))
|
||||
}
|
||||
func GetConversationIDBySessionType(sourceID string, sessionType int) string {
|
||||
switch sessionType {
|
||||
case constant.SingleChatType:
|
||||
return "single_" + sourceID
|
||||
case constant.GroupChatType:
|
||||
return "group_" + sourceID
|
||||
case constant.NotificationChatType:
|
||||
return "notification_" + sourceID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func int64ToString(i int64) string {
|
||||
return strconv.FormatInt(i, 10)
|
||||
}
|
||||
func Int64ToString(i int64) string {
|
||||
return strconv.FormatInt(i, 10)
|
||||
}
|
||||
|
||||
func RemoveDuplicateElement(idList []string) []string {
|
||||
result := make([]string, 0, len(idList))
|
||||
temp := map[string]struct{}{}
|
||||
for _, item := range idList {
|
||||
if _, ok := temp[item]; !ok {
|
||||
temp[item] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func RemoveDuplicate[T comparable](arr []T) []T {
|
||||
result := make([]T, 0, len(arr))
|
||||
temp := map[T]struct{}{}
|
||||
for _, item := range arr {
|
||||
if _, ok := temp[item]; !ok {
|
||||
temp[item] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func IsDuplicateStringSlice(arr []string) bool {
|
||||
t := make(map[string]struct{})
|
||||
for _, s := range arr {
|
||||
if _, ok := t[s]; ok {
|
||||
return true
|
||||
}
|
||||
t[s] = struct{}{}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -83,3 +83,7 @@ func TimeStringToTime(timeString string) (time.Time, error) {
|
||||
t, err := time.Parse("2006-01-02", timeString)
|
||||
return t, err
|
||||
}
|
||||
|
||||
func TimeToString(t time.Time) string {
|
||||
return t.Format("2006-01-02")
|
||||
}
|
||||
|
||||
+263
-22
@@ -1,13 +1,20 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"hash/crc32"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// copy a by b b->a
|
||||
@@ -15,6 +22,24 @@ func CopyStructFields(a interface{}, b interface{}, fields ...string) (err error
|
||||
return copier.Copy(a, b)
|
||||
}
|
||||
|
||||
func Wrap1(err error) error {
|
||||
return errors.Wrap(err, "==> "+printCallerNameAndLine())
|
||||
}
|
||||
|
||||
func Wrap2[T any](a T, err error) (T, error) {
|
||||
if err != nil {
|
||||
return a, errors.Wrap(err, "==> "+printCallerNameAndLine())
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func Wrap3[T any, V any](a T, b V, err error) (T, V, error) {
|
||||
if err != nil {
|
||||
return a, b, errors.Wrap(err, "==> "+printCallerNameAndLine())
|
||||
}
|
||||
return a, b, nil
|
||||
}
|
||||
|
||||
func Wrap(err error, message string) error {
|
||||
return errors.Wrap(err, "==> "+printCallerNameAndLine()+message)
|
||||
}
|
||||
@@ -32,6 +57,16 @@ func GetSelfFuncName() string {
|
||||
pc, _, _, _ := runtime.Caller(1)
|
||||
return cleanUpFuncName(runtime.FuncForPC(pc).Name())
|
||||
}
|
||||
|
||||
func GetFuncName(skips ...int) string {
|
||||
skip := 1
|
||||
if len(skips) > 0 {
|
||||
skip = skips[0] + 1
|
||||
}
|
||||
pc, _, _, _ := runtime.Caller(skip)
|
||||
return cleanUpFuncName(runtime.FuncForPC(pc).Name())
|
||||
}
|
||||
|
||||
func cleanUpFuncName(funcName string) string {
|
||||
end := strings.LastIndex(funcName, ".")
|
||||
if end == -1 {
|
||||
@@ -40,10 +75,10 @@ func cleanUpFuncName(funcName string) string {
|
||||
return funcName[end+1:]
|
||||
}
|
||||
|
||||
//Get the intersection of two slices
|
||||
func Intersect(slice1, slice2 []uint32) []uint32 {
|
||||
m := make(map[uint32]bool)
|
||||
n := make([]uint32, 0)
|
||||
// Get the intersection of two slices
|
||||
func Intersect(slice1, slice2 []int64) []int64 {
|
||||
m := make(map[int64]bool)
|
||||
n := make([]int64, 0)
|
||||
for _, v := range slice1 {
|
||||
m[v] = true
|
||||
}
|
||||
@@ -56,10 +91,10 @@ func Intersect(slice1, slice2 []uint32) []uint32 {
|
||||
return n
|
||||
}
|
||||
|
||||
//Get the diff of two slices
|
||||
func Difference(slice1, slice2 []uint32) []uint32 {
|
||||
m := make(map[uint32]bool)
|
||||
n := make([]uint32, 0)
|
||||
// Get the diff of two slices
|
||||
func Difference(slice1, slice2 []int64) []int64 {
|
||||
m := make(map[int64]bool)
|
||||
n := make([]int64, 0)
|
||||
inter := Intersect(slice1, slice2)
|
||||
for _, v := range inter {
|
||||
m[v] = true
|
||||
@@ -78,7 +113,7 @@ func Difference(slice1, slice2 []uint32) []uint32 {
|
||||
return n
|
||||
}
|
||||
|
||||
//Get the intersection of two slices
|
||||
// Get the intersection of two slices
|
||||
func IntersectString(slice1, slice2 []string) []string {
|
||||
m := make(map[string]bool)
|
||||
n := make([]string, 0)
|
||||
@@ -94,7 +129,7 @@ func IntersectString(slice1, slice2 []string) []string {
|
||||
return n
|
||||
}
|
||||
|
||||
//Get the diff of two slices
|
||||
// Get the diff of two slices
|
||||
func DifferenceString(slice1, slice2 []string) []string {
|
||||
m := make(map[string]bool)
|
||||
n := make([]string, 0)
|
||||
@@ -115,19 +150,225 @@ func DifferenceString(slice1, slice2 []string) []string {
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func OperationIDGenerator() string {
|
||||
return strconv.FormatInt(time.Now().UnixNano()+int64(rand.Uint32()), 10)
|
||||
}
|
||||
|
||||
func RemoveRepeatedStringInList(slc []string) []string {
|
||||
var result []string
|
||||
tempMap := map[string]byte{}
|
||||
for _, e := range slc {
|
||||
l := len(tempMap)
|
||||
tempMap[e] = 0
|
||||
if len(tempMap) != l {
|
||||
result = append(result, e)
|
||||
}
|
||||
func Pb2String(pb proto.Message) (string, error) {
|
||||
s, err := proto.Marshal(pb)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return result
|
||||
return string(s), nil
|
||||
}
|
||||
|
||||
func String2Pb(s string, pb proto.Message) error {
|
||||
return proto.Unmarshal([]byte(s), pb)
|
||||
}
|
||||
|
||||
func GetHashCode(s string) uint32 {
|
||||
return crc32.ChecksumIEEE([]byte(s))
|
||||
}
|
||||
|
||||
func GetNotificationConversationID(msg *sdkws.MsgData) string {
|
||||
switch msg.SessionType {
|
||||
case constant.SingleChatType:
|
||||
l := []string{msg.SendID, msg.RecvID}
|
||||
sort.Strings(l)
|
||||
return "n_" + strings.Join(l, "_")
|
||||
case constant.GroupChatType:
|
||||
return "n_" + msg.GroupID
|
||||
case constant.SuperGroupChatType:
|
||||
return "n_" + msg.GroupID
|
||||
case constant.NotificationChatType:
|
||||
return "n_" + msg.SendID + "_" + msg.RecvID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetChatConversationIDByMsg(msg *sdkws.MsgData) string {
|
||||
switch msg.SessionType {
|
||||
case constant.SingleChatType:
|
||||
l := []string{msg.SendID, msg.RecvID}
|
||||
sort.Strings(l)
|
||||
return "si_" + strings.Join(l, "_")
|
||||
case constant.GroupChatType:
|
||||
return "g_" + msg.GroupID
|
||||
case constant.SuperGroupChatType:
|
||||
return "sg_" + msg.GroupID
|
||||
case constant.NotificationChatType:
|
||||
return "sn_" + msg.SendID + "_" + msg.RecvID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GenConversationIDForSingle(sendID, recvID string) string {
|
||||
l := []string{sendID, recvID}
|
||||
sort.Strings(l)
|
||||
return "si_" + strings.Join(l, "_")
|
||||
}
|
||||
|
||||
func GenConversationUniqueKeyForGroup(groupID string) string {
|
||||
return groupID
|
||||
}
|
||||
|
||||
func GenGroupConversationID(groupID string) string {
|
||||
return "sg_" + groupID
|
||||
}
|
||||
|
||||
func GenConversationUniqueKeyForSingle(sendID, recvID string) string {
|
||||
l := []string{sendID, recvID}
|
||||
sort.Strings(l)
|
||||
return strings.Join(l, "_")
|
||||
}
|
||||
|
||||
func GenConversationUniqueKey(msg *sdkws.MsgData) string {
|
||||
switch msg.SessionType {
|
||||
case constant.SingleChatType, constant.NotificationChatType:
|
||||
l := []string{msg.SendID, msg.RecvID}
|
||||
sort.Strings(l)
|
||||
return strings.Join(l, "_")
|
||||
case constant.SuperGroupChatType:
|
||||
return msg.GroupID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetConversationIDByMsgModel(msg *unrelation.MsgDataModel) string {
|
||||
options := Options(msg.Options)
|
||||
switch msg.SessionType {
|
||||
case constant.SingleChatType:
|
||||
l := []string{msg.SendID, msg.RecvID}
|
||||
sort.Strings(l)
|
||||
if !options.IsNotNotification() {
|
||||
return "n_" + strings.Join(l, "_")
|
||||
}
|
||||
return "si_" + strings.Join(l, "_") // single chat
|
||||
case constant.GroupChatType:
|
||||
if !options.IsNotNotification() {
|
||||
return "n_" + msg.GroupID // group chat
|
||||
}
|
||||
return "g_" + msg.GroupID // group chat
|
||||
case constant.SuperGroupChatType:
|
||||
if !options.IsNotNotification() {
|
||||
return "n_" + msg.GroupID // super group chat
|
||||
}
|
||||
return "sg_" + msg.GroupID // super group chat
|
||||
case constant.NotificationChatType:
|
||||
if !options.IsNotNotification() {
|
||||
return "n_" + msg.SendID + "_" + msg.RecvID // super group chat
|
||||
}
|
||||
return "sn_" + msg.SendID + "_" + msg.RecvID // server notification chat
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetConversationIDByMsg(msg *sdkws.MsgData) string {
|
||||
options := Options(msg.Options)
|
||||
switch msg.SessionType {
|
||||
case constant.SingleChatType:
|
||||
l := []string{msg.SendID, msg.RecvID}
|
||||
sort.Strings(l)
|
||||
if !options.IsNotNotification() {
|
||||
return "n_" + strings.Join(l, "_")
|
||||
}
|
||||
return "si_" + strings.Join(l, "_") // single chat
|
||||
case constant.GroupChatType:
|
||||
if !options.IsNotNotification() {
|
||||
return "n_" + msg.GroupID // group chat
|
||||
}
|
||||
return "g_" + msg.GroupID // group chat
|
||||
case constant.SuperGroupChatType:
|
||||
if !options.IsNotNotification() {
|
||||
return "n_" + msg.GroupID // super group chat
|
||||
}
|
||||
return "sg_" + msg.GroupID // super group chat
|
||||
case constant.NotificationChatType:
|
||||
if !options.IsNotNotification() {
|
||||
return "n_" + msg.SendID + "_" + msg.RecvID // super group chat
|
||||
}
|
||||
return "sn_" + msg.SendID + "_" + msg.RecvID // server notification chat
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetConversationIDBySessionType(sessionType int, ids ...string) string {
|
||||
sort.Strings(ids)
|
||||
if len(ids) > 2 || len(ids) < 1 {
|
||||
return ""
|
||||
}
|
||||
switch sessionType {
|
||||
case constant.SingleChatType:
|
||||
return "si_" + strings.Join(ids, "_") // single chat
|
||||
case constant.GroupChatType:
|
||||
return "g_" + ids[0] // group chat
|
||||
case constant.SuperGroupChatType:
|
||||
return "sg_" + ids[0] // super group chat
|
||||
case constant.NotificationChatType:
|
||||
return "sn_" + ids[0] // server notification chat
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func IsNotification(conversationID string) bool {
|
||||
return strings.HasPrefix(conversationID, "n_")
|
||||
}
|
||||
|
||||
func IsNotificationByMsg(msg *sdkws.MsgData) bool {
|
||||
return !Options(msg.Options).IsNotNotification()
|
||||
}
|
||||
|
||||
func ParseConversationID(msg *sdkws.MsgData) (isNotification bool, conversationID string) {
|
||||
options := Options(msg.Options)
|
||||
switch msg.SessionType {
|
||||
case constant.SingleChatType:
|
||||
l := []string{msg.SendID, msg.RecvID}
|
||||
sort.Strings(l)
|
||||
if !options.IsNotNotification() {
|
||||
return true, "n_" + strings.Join(l, "_")
|
||||
}
|
||||
return false, "si_" + strings.Join(l, "_") // single chat
|
||||
case constant.SuperGroupChatType:
|
||||
if !options.IsNotNotification() {
|
||||
return true, "n_" + msg.GroupID // super group chat
|
||||
}
|
||||
return false, "sg_" + msg.GroupID // super group chat
|
||||
case constant.NotificationChatType:
|
||||
if !options.IsNotNotification() {
|
||||
return true, "n_" + msg.SendID + "_" + msg.RecvID // super group chat
|
||||
}
|
||||
return false, "sn_" + msg.SendID + "_" + msg.RecvID // server notification chat
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func GetNotificationConversationIDByConversationID(conversationID string) string {
|
||||
l := strings.Split(conversationID, "_")
|
||||
if len(l) > 1 {
|
||||
l[0] = "n"
|
||||
return strings.Join(l, "_")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetSeqsBeginEnd(seqs []int64) (int64, int64) {
|
||||
if len(seqs) == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
return seqs[0], seqs[len(seqs)-1]
|
||||
}
|
||||
|
||||
type MsgBySeq []*sdkws.MsgData
|
||||
|
||||
func (s MsgBySeq) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s MsgBySeq) Less(i, j int) bool {
|
||||
return s[i].Seq < s[j].Seq
|
||||
}
|
||||
|
||||
func (s MsgBySeq) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,531 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// SliceSub a中存在,b中不存在 (a-b)
|
||||
func SliceSub[E comparable](a, b []E) []E {
|
||||
k := make(map[E]struct{})
|
||||
for i := 0; i < len(b); i++ {
|
||||
k[b[i]] = struct{}{}
|
||||
}
|
||||
t := make(map[E]struct{})
|
||||
rs := make([]E, 0, len(a))
|
||||
for i := 0; i < len(a); i++ {
|
||||
e := a[i]
|
||||
if _, ok := t[e]; ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := k[e]; ok {
|
||||
continue
|
||||
}
|
||||
rs = append(rs, e)
|
||||
t[e] = struct{}{}
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
// SliceSubAny a中存在,b中不存在 (a-b)
|
||||
func SliceSubAny[E comparable, T any](a []E, b []T, fn func(t T) E) []E {
|
||||
return SliceSub(a, Slice(b, fn))
|
||||
}
|
||||
|
||||
// SliceAnySub a中存在,b中不存在 (a-b) fn 返回的是uuid
|
||||
func SliceAnySub[E any, T comparable](a, b []E, fn func(t E) T) []E {
|
||||
m := make(map[T]E)
|
||||
for i := 0; i < len(b); i++ {
|
||||
v := b[i]
|
||||
m[fn(v)] = v
|
||||
}
|
||||
var es []E
|
||||
for i := 0; i < len(a); i++ {
|
||||
v := a[i]
|
||||
if _, ok := m[fn(v)]; !ok {
|
||||
es = append(es, v)
|
||||
}
|
||||
}
|
||||
return es
|
||||
}
|
||||
|
||||
// DistinctAny 去重
|
||||
func DistinctAny[E any, K comparable](es []E, fn func(e E) K) []E {
|
||||
v := make([]E, 0, len(es))
|
||||
tmp := map[K]struct{}{}
|
||||
for i := 0; i < len(es); i++ {
|
||||
t := es[i]
|
||||
k := fn(t)
|
||||
if _, ok := tmp[k]; !ok {
|
||||
tmp[k] = struct{}{}
|
||||
v = append(v, t)
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func DistinctAnyGetComparable[E any, K comparable](es []E, fn func(e E) K) []K {
|
||||
v := make([]K, 0, len(es))
|
||||
tmp := map[K]struct{}{}
|
||||
for i := 0; i < len(es); i++ {
|
||||
t := es[i]
|
||||
k := fn(t)
|
||||
if _, ok := tmp[k]; !ok {
|
||||
tmp[k] = struct{}{}
|
||||
v = append(v, k)
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Distinct 去重
|
||||
func Distinct[T comparable](ts []T) []T {
|
||||
if len(ts) < 2 {
|
||||
return ts
|
||||
} else if len(ts) == 2 {
|
||||
if ts[0] == ts[1] {
|
||||
return ts[:1]
|
||||
} else {
|
||||
return ts
|
||||
}
|
||||
}
|
||||
return DistinctAny(ts, func(t T) T {
|
||||
return t
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除切片元素, 支持负数删除倒数第几个
|
||||
func Delete[E any](es []E, index ...int) []E {
|
||||
switch len(index) {
|
||||
case 0:
|
||||
return es
|
||||
case 1:
|
||||
i := index[0]
|
||||
if i < 0 {
|
||||
i = len(es) + i
|
||||
}
|
||||
if len(es) <= i {
|
||||
return es
|
||||
}
|
||||
return append(es[:i], es[i+1:]...)
|
||||
default:
|
||||
tmp := make(map[int]struct{})
|
||||
for _, i := range index {
|
||||
if i < 0 {
|
||||
i = len(es) + i
|
||||
}
|
||||
tmp[i] = struct{}{}
|
||||
}
|
||||
v := make([]E, 0, len(es))
|
||||
for i := 0; i < len(es); i++ {
|
||||
if _, ok := tmp[i]; !ok {
|
||||
v = append(v, es[i])
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteAt 删除切片元素, 支持负数删除倒数第几个
|
||||
func DeleteAt[E any](es *[]E, index ...int) []E {
|
||||
v := Delete(*es, index...)
|
||||
*es = v
|
||||
return v
|
||||
}
|
||||
|
||||
// IndexAny get the index of the element
|
||||
func IndexAny[E any, K comparable](e E, es []E, fn func(e E) K) int {
|
||||
k := fn(e)
|
||||
for i := 0; i < len(es); i++ {
|
||||
if fn(es[i]) == k {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// IndexOf get the index of the element
|
||||
func IndexOf[E comparable](e E, es ...E) int {
|
||||
return IndexAny(e, es, func(t E) E {
|
||||
return t
|
||||
})
|
||||
}
|
||||
|
||||
// Contain 是否包含
|
||||
func Contain[E comparable](e E, es ...E) bool {
|
||||
return IndexOf(e, es...) >= 0
|
||||
}
|
||||
|
||||
// DuplicateAny 是否有重复的
|
||||
func DuplicateAny[E any, K comparable](es []E, fn func(e E) K) bool {
|
||||
t := make(map[K]struct{})
|
||||
for _, e := range es {
|
||||
k := fn(e)
|
||||
if _, ok := t[k]; ok {
|
||||
return true
|
||||
}
|
||||
t[k] = struct{}{}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Duplicate 是否有重复的
|
||||
func Duplicate[E comparable](es []E) bool {
|
||||
return DuplicateAny(es, func(e E) E {
|
||||
return e
|
||||
})
|
||||
}
|
||||
|
||||
// SliceToMapOkAny slice to map (自定义类型, 筛选)
|
||||
func SliceToMapOkAny[E any, K comparable, V any](es []E, fn func(e E) (K, V, bool)) map[K]V {
|
||||
kv := make(map[K]V)
|
||||
for i := 0; i < len(es); i++ {
|
||||
t := es[i]
|
||||
if k, v, ok := fn(t); ok {
|
||||
kv[k] = v
|
||||
}
|
||||
}
|
||||
return kv
|
||||
}
|
||||
|
||||
// SliceToMapAny slice to map (自定义类型)
|
||||
func SliceToMapAny[E any, K comparable, V any](es []E, fn func(e E) (K, V)) map[K]V {
|
||||
return SliceToMapOkAny(es, func(e E) (K, V, bool) {
|
||||
k, v := fn(e)
|
||||
return k, v, true
|
||||
})
|
||||
}
|
||||
|
||||
// SliceToMap slice to map
|
||||
func SliceToMap[E any, K comparable](es []E, fn func(e E) K) map[K]E {
|
||||
return SliceToMapOkAny(es, func(e E) (K, E, bool) {
|
||||
k := fn(e)
|
||||
return k, e, true
|
||||
})
|
||||
}
|
||||
|
||||
// SliceSetAny slice to map[K]struct{}
|
||||
func SliceSetAny[E any, K comparable](es []E, fn func(e E) K) map[K]struct{} {
|
||||
return SliceToMapAny(es, func(e E) (K, struct{}) {
|
||||
return fn(e), struct{}{}
|
||||
})
|
||||
}
|
||||
|
||||
func Filter[E, T any](es []E, fn func(e E) (T, bool)) []T {
|
||||
rs := make([]T, 0, len(es))
|
||||
for i := 0; i < len(es); i++ {
|
||||
e := es[i]
|
||||
if t, ok := fn(e); ok {
|
||||
rs = append(rs, t)
|
||||
}
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
// Slice 批量转换切片类型
|
||||
func Slice[E any, T any](es []E, fn func(e E) T) []T {
|
||||
v := make([]T, len(es))
|
||||
for i := 0; i < len(es); i++ {
|
||||
v[i] = fn(es[i])
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// SliceSet slice to map[E]struct{}
|
||||
func SliceSet[E comparable](es []E) map[E]struct{} {
|
||||
return SliceSetAny(es, func(e E) E {
|
||||
return e
|
||||
})
|
||||
}
|
||||
|
||||
// HasKey get whether the map contains key
|
||||
func HasKey[K comparable, V any](m map[K]V, k K) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := m[k]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Min get minimum value
|
||||
func Min[E Ordered](e ...E) E {
|
||||
v := e[0]
|
||||
for _, t := range e[1:] {
|
||||
if v > t {
|
||||
v = t
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Max get maximum value
|
||||
func Max[E Ordered](e ...E) E {
|
||||
v := e[0]
|
||||
for _, t := range e[1:] {
|
||||
if v < t {
|
||||
v = t
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func Paginate[E any](es []E, pageNumber int, showNumber int) []E {
|
||||
if pageNumber <= 0 {
|
||||
return []E{}
|
||||
}
|
||||
if showNumber <= 0 {
|
||||
return []E{}
|
||||
}
|
||||
start := (pageNumber - 1) * showNumber
|
||||
end := start + showNumber
|
||||
if start >= len(es) {
|
||||
return []E{}
|
||||
}
|
||||
if end > len(es) {
|
||||
end = len(es)
|
||||
}
|
||||
return es[start:end]
|
||||
}
|
||||
|
||||
// BothExistAny 获取切片中共同存在的元素(交集)
|
||||
func BothExistAny[E any, K comparable](es [][]E, fn func(e E) K) []E {
|
||||
if len(es) == 0 {
|
||||
return []E{}
|
||||
}
|
||||
var idx int
|
||||
ei := make([]map[K]E, len(es))
|
||||
for i := 0; i < len(ei); i++ {
|
||||
e := es[i]
|
||||
if len(e) == 0 {
|
||||
return []E{}
|
||||
}
|
||||
kv := make(map[K]E)
|
||||
for j := 0; j < len(e); j++ {
|
||||
t := e[j]
|
||||
k := fn(t)
|
||||
kv[k] = t
|
||||
}
|
||||
ei[i] = kv
|
||||
if len(kv) < len(ei[idx]) {
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
v := make([]E, 0, len(ei[idx]))
|
||||
for k := range ei[idx] {
|
||||
all := true
|
||||
for i := 0; i < len(ei); i++ {
|
||||
if i == idx {
|
||||
continue
|
||||
}
|
||||
if _, ok := ei[i][k]; !ok {
|
||||
all = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !all {
|
||||
continue
|
||||
}
|
||||
v = append(v, ei[idx][k])
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// BothExist 获取切片中共同存在的元素(交集)
|
||||
func BothExist[E comparable](es ...[]E) []E {
|
||||
return BothExistAny(es, func(e E) E {
|
||||
return e
|
||||
})
|
||||
}
|
||||
|
||||
//// CompleteAny a中存在b的所有元素, 同时b中的所有元素a
|
||||
//func CompleteAny[K comparable, E any](ks []K, es []E, fn func(e E) K) bool {
|
||||
// if len(ks) == 0 && len(es) == 0 {
|
||||
// return true
|
||||
// }
|
||||
// kn := make(map[K]uint8)
|
||||
// for _, e := range Distinct(ks) {
|
||||
// kn[e]++
|
||||
// }
|
||||
// for k := range SliceSetAny(es, fn) {
|
||||
// kn[k]++
|
||||
// }
|
||||
// for _, n := range kn {
|
||||
// if n != 2 {
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
// return true
|
||||
//}
|
||||
|
||||
// Complete a和b去重后是否相等(忽略顺序)
|
||||
func Complete[E comparable](a []E, b []E) bool {
|
||||
return len(Single(a, b)) == 0
|
||||
}
|
||||
|
||||
// Keys get map keys
|
||||
func Keys[K comparable, V any](kv map[K]V) []K {
|
||||
ks := make([]K, 0, len(kv))
|
||||
for k := range kv {
|
||||
ks = append(ks, k)
|
||||
}
|
||||
return ks
|
||||
}
|
||||
|
||||
// Values get map values
|
||||
func Values[K comparable, V any](kv map[K]V) []V {
|
||||
vs := make([]V, 0, len(kv))
|
||||
for k := range kv {
|
||||
vs = append(vs, kv[k])
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// Sort basic type sorting
|
||||
func Sort[E Ordered](es []E, asc bool) []E {
|
||||
SortAny(es, func(a, b E) bool {
|
||||
if asc {
|
||||
return a < b
|
||||
} else {
|
||||
return a > b
|
||||
}
|
||||
})
|
||||
return es
|
||||
}
|
||||
|
||||
// SortAny custom sort method
|
||||
func SortAny[E any](es []E, fn func(a, b E) bool) {
|
||||
sort.Sort(&sortSlice[E]{
|
||||
ts: es,
|
||||
fn: fn,
|
||||
})
|
||||
}
|
||||
|
||||
// If true -> a, false -> b
|
||||
func If[T any](isa bool, a, b T) T {
|
||||
if isa {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func ToPtr[T any](t T) *T {
|
||||
return &t
|
||||
}
|
||||
|
||||
// Equal 比较切片是否相对(包括元素顺序)
|
||||
func Equal[E comparable](a []E, b []E) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Single a中存在,b中不存在 或 b中存在,a中不存在
|
||||
func Single[E comparable](a, b []E) []E {
|
||||
kn := make(map[E]uint8)
|
||||
for _, e := range Distinct(a) {
|
||||
kn[e]++
|
||||
}
|
||||
for _, e := range Distinct(b) {
|
||||
kn[e]++
|
||||
}
|
||||
v := make([]E, 0, len(kn))
|
||||
for k, n := range kn {
|
||||
if n == 1 {
|
||||
v = append(v, k)
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Order 将ts按es排序
|
||||
func Order[E comparable, T any](es []E, ts []T, fn func(t T) E) []T {
|
||||
if len(es) == 0 || len(ts) == 0 {
|
||||
return ts
|
||||
}
|
||||
kv := make(map[E][]T)
|
||||
for i := 0; i < len(ts); i++ {
|
||||
t := ts[i]
|
||||
k := fn(t)
|
||||
kv[k] = append(kv[k], t)
|
||||
}
|
||||
rs := make([]T, 0, len(ts))
|
||||
for _, e := range es {
|
||||
vs := kv[e]
|
||||
delete(kv, e)
|
||||
rs = append(rs, vs...)
|
||||
}
|
||||
for k := range kv {
|
||||
rs = append(rs, kv[k]...)
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
func OrderPtr[E comparable, T any](es []E, ts *[]T, fn func(t T) E) []T {
|
||||
*ts = Order(es, *ts, fn)
|
||||
return *ts
|
||||
}
|
||||
|
||||
func UniqueJoin(s ...string) string {
|
||||
data, _ := json.Marshal(s)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
type sortSlice[E any] struct {
|
||||
ts []E
|
||||
fn func(a, b E) bool
|
||||
}
|
||||
|
||||
func (o *sortSlice[E]) Len() int {
|
||||
return len(o.ts)
|
||||
}
|
||||
|
||||
func (o *sortSlice[E]) Less(i, j int) bool {
|
||||
return o.fn(o.ts[i], o.ts[j])
|
||||
}
|
||||
|
||||
func (o *sortSlice[E]) Swap(i, j int) {
|
||||
o.ts[i], o.ts[j] = o.ts[j], o.ts[i]
|
||||
}
|
||||
|
||||
// Ordered types that can be sorted
|
||||
type Ordered interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 | ~string
|
||||
}
|
||||
|
||||
func Unwrap(err error) error {
|
||||
for err != nil {
|
||||
unwrap, ok := err.(interface {
|
||||
Unwrap() error
|
||||
})
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
err = unwrap.Unwrap()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NotNilReplace 当new_不为空时, 将old设置为new_
|
||||
func NotNilReplace[T any](old, new_ *T) {
|
||||
if new_ == nil {
|
||||
return
|
||||
}
|
||||
*old = *new_
|
||||
}
|
||||
|
||||
func Batch[T any, V any](fn func(T) V, ts []T) []V {
|
||||
if ts == nil {
|
||||
return nil
|
||||
}
|
||||
res := make([]V, 0, len(ts))
|
||||
for i := range ts {
|
||||
res = append(res, fn(ts[i]))
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDistinct(t *testing.T) {
|
||||
arr := []int{1, 1, 1, 4, 4, 5, 2, 3, 3, 3, 6}
|
||||
fmt.Println(Distinct(arr))
|
||||
}
|
||||
|
||||
func TestDeleteAt(t *testing.T) {
|
||||
arr := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
fmt.Println(Delete(arr, 0, 1, -1, -2))
|
||||
fmt.Println(Delete(arr))
|
||||
fmt.Println(Delete(arr, 1))
|
||||
}
|
||||
|
||||
func TestSliceToMap(t *testing.T) {
|
||||
type Item struct {
|
||||
ID string
|
||||
Name string
|
||||
}
|
||||
list := []Item{
|
||||
{ID: "111", Name: "111"},
|
||||
{ID: "222", Name: "222"},
|
||||
{ID: "333", Name: "333"},
|
||||
}
|
||||
|
||||
m := SliceToMap(list, func(t Item) string {
|
||||
return t.ID
|
||||
})
|
||||
|
||||
fmt.Printf("%+v\n", m)
|
||||
|
||||
}
|
||||
|
||||
func TestIndexOf(t *testing.T) {
|
||||
arr := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
|
||||
fmt.Println(IndexOf(3, arr...))
|
||||
|
||||
}
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
arr := []int{1, 1, 1, 4, 4, 5, 2, 3, 3, 3, 6}
|
||||
fmt.Println(Sort(arr, false))
|
||||
}
|
||||
|
||||
func TestBothExist(t *testing.T) {
|
||||
arr1 := []int{1, 1, 1, 4, 4, 5, 2, 3, 3, 3, 6}
|
||||
arr2 := []int{6, 1, 3}
|
||||
arr3 := []int{5, 1, 3, 6}
|
||||
fmt.Println(BothExist(arr1, arr2, arr3))
|
||||
}
|
||||
|
||||
func TestCompleteAny(t *testing.T) {
|
||||
type Item struct {
|
||||
ID int
|
||||
Value string
|
||||
}
|
||||
|
||||
ids := []int{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
|
||||
var list []Item
|
||||
|
||||
for _, id := range ids {
|
||||
list = append(list, Item{
|
||||
ID: id,
|
||||
Value: fmt.Sprintf("%d", id*1000),
|
||||
})
|
||||
}
|
||||
|
||||
DeleteAt(&list, -1)
|
||||
DeleteAt(&ids, -1)
|
||||
|
||||
ok := Complete(ids, Slice(list, func(t Item) int {
|
||||
return t.ID
|
||||
}))
|
||||
|
||||
fmt.Printf("%+v\n", ok)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user