feat: use robot to migrate code

Signed-off-by: kubbot & kubecub <3293172751ysy@gmail.com>
This commit is contained in:
kubbot & kubecub
2023-06-30 09:45:02 +08:00
parent 2d41819008
commit 539e0fdfb6
529 changed files with 64588 additions and 54413 deletions
+18
View File
@@ -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)
}
}
-24
View File
@@ -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
View File
@@ -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
View File
@@ -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")
}
+28
View File
@@ -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{}
}
+15
View File
@@ -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
View File
@@ -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
}
+27
View File
@@ -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)
}
+1
View File
@@ -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)
+15
View File
@@ -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)
}
+167
View File
@@ -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)
}
+10
View File
@@ -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(""), "")
}
+184
View File
@@ -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
}
+56
View File
@@ -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)
}
+28
View File
@@ -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
View File
@@ -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
}
+4
View File
@@ -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
View File
@@ -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]
}
+531
View File
@@ -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
}
+84
View File
@@ -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)
}