485 lines
14 KiB
Go
485 lines
14 KiB
Go
// Copyright © 2023 OpenIM SDK. All rights reserved.
|
||
//
|
||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
// you may not use this file except in compliance with the License.
|
||
// You may obtain a copy of the License at
|
||
//
|
||
// http://www.apache.org/licenses/LICENSE-2.0
|
||
//
|
||
// Unless required by applicable law or agreed to in writing, software
|
||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
// See the License for the specific language governing permissions and
|
||
// limitations under the License.
|
||
|
||
package open_im_sdk
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
|
||
"github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
|
||
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
|
||
"reflect"
|
||
"runtime"
|
||
"runtime/debug"
|
||
"time"
|
||
|
||
"github.com/openimsdk/tools/log"
|
||
|
||
"github.com/openimsdk/tools/errs"
|
||
)
|
||
|
||
func isNumeric(kind reflect.Kind) bool {
|
||
switch kind {
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||
reflect.Float32, reflect.Float64:
|
||
return true
|
||
default:
|
||
return false
|
||
}
|
||
}
|
||
|
||
func setNumeric(in interface{}, out interface{}) {
|
||
inValue := reflect.ValueOf(in)
|
||
outValue := reflect.ValueOf(out)
|
||
outElem := outValue.Elem()
|
||
outType := outElem.Type()
|
||
inType := inValue.Type()
|
||
if outType.AssignableTo(inType) {
|
||
outElem.Set(inValue)
|
||
return
|
||
}
|
||
inKind := inValue.Kind()
|
||
outKind := outElem.Kind()
|
||
switch inKind {
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
switch outKind {
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
outElem.SetInt(inValue.Int())
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
outElem.SetUint(uint64(inValue.Int()))
|
||
case reflect.Float32, reflect.Float64:
|
||
outElem.SetFloat(float64(inValue.Int()))
|
||
}
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
switch outKind {
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
outElem.SetInt(int64(inValue.Uint()))
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
outElem.SetUint(inValue.Uint())
|
||
case reflect.Float32, reflect.Float64:
|
||
outElem.SetFloat(float64(inValue.Uint()))
|
||
}
|
||
case reflect.Float32, reflect.Float64:
|
||
switch outKind {
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
outElem.SetInt(int64(inValue.Float()))
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
outElem.SetUint(uint64(inValue.Float()))
|
||
case reflect.Float32, reflect.Float64:
|
||
outElem.SetFloat(inValue.Float())
|
||
}
|
||
}
|
||
}
|
||
|
||
func call_(operationID string, fn any, args ...any) (res any, err error) {
|
||
t := time.Now()
|
||
funcPtr := reflect.ValueOf(fn).Pointer()
|
||
funcName := runtime.FuncForPC(funcPtr).Name()
|
||
if operationID == "" {
|
||
return nil, sdkerrs.ErrArgs.WrapMsg("call function operationID is empty")
|
||
}
|
||
if err := CheckResourceLoad(UserForSDK, funcName); err != nil {
|
||
return nil, sdkerrs.ErrResourceLoad.WrapMsg("not load resource")
|
||
}
|
||
ctx := ccontext.WithOperationID(UserForSDK.BaseCtx(), operationID)
|
||
defer func(start time.Time) {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf("panic: %+v\n%s", r, debug.Stack())
|
||
err = fmt.Errorf("call panic: %+v", r)
|
||
} else {
|
||
elapsed := time.Since(start).Milliseconds()
|
||
if err == nil {
|
||
log.ZInfo(ctx, "fn call success", "function name", funcName, "resp", res, "cost time", fmt.Sprintf("%d ms", elapsed))
|
||
} else {
|
||
log.ZError(ctx, "fn call error", err, "function name", funcName, "cost time", fmt.Sprintf("%d ms", elapsed))
|
||
|
||
}
|
||
|
||
}
|
||
}(t)
|
||
log.ZInfo(ctx, "func call req", "function name", funcName, "args", args)
|
||
fnv := reflect.ValueOf(fn)
|
||
if fnv.Kind() != reflect.Func {
|
||
return nil, sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("call function fn is not function, is %T", fn))
|
||
}
|
||
fnt := fnv.Type()
|
||
nin := fnt.NumIn()
|
||
if len(args)+1 != nin {
|
||
return nil, sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("go code error: fn in args num is not match"))
|
||
}
|
||
ins := make([]reflect.Value, 0, nin)
|
||
ins = append(ins, reflect.ValueOf(ctx))
|
||
for i := 0; i < len(args); i++ {
|
||
inFnField := fnt.In(i + 1)
|
||
arg := reflect.TypeOf(args[i])
|
||
if arg.String() == inFnField.String() || inFnField.Kind() == reflect.Interface {
|
||
ins = append(ins, reflect.ValueOf(args[i]))
|
||
continue
|
||
}
|
||
if arg.Kind() == reflect.String { // json
|
||
var ptr int
|
||
for inFnField.Kind() == reflect.Ptr {
|
||
inFnField = inFnField.Elem()
|
||
ptr++
|
||
}
|
||
switch inFnField.Kind() {
|
||
case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
|
||
v := reflect.New(inFnField)
|
||
if err := json.Unmarshal([]byte(args[i].(string)), v.Interface()); err != nil {
|
||
return nil, sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("go call json.Unmarshal error: %s", err))
|
||
}
|
||
if ptr == 0 {
|
||
v = v.Elem()
|
||
} else if ptr != 1 {
|
||
for i := ptr - 1; i > 0; i-- {
|
||
temp := reflect.New(v.Type())
|
||
temp.Elem().Set(v)
|
||
v = temp
|
||
}
|
||
}
|
||
ins = append(ins, v)
|
||
continue
|
||
}
|
||
}
|
||
//if isNumeric(arg.Kind()) && isNumeric(inFnField.Kind()) {
|
||
// v := reflect.Zero(inFnField).Interface()
|
||
// setNumeric(args[i], &v)
|
||
// ins = append(ins, reflect.ValueOf(v))
|
||
// continue
|
||
//}
|
||
return nil, sdkerrs.ErrSdkInternal.WrapMsg(fmt.Sprintf("go code error: fn in args type is not match"))
|
||
}
|
||
outs := fnv.Call(ins)
|
||
if len(outs) == 0 {
|
||
return "", nil
|
||
}
|
||
if fnt.Out(len(outs) - 1).Implements(reflect.ValueOf(new(error)).Elem().Type()) {
|
||
if errValueOf := outs[len(outs)-1]; !errValueOf.IsNil() {
|
||
return nil, errValueOf.Interface().(error)
|
||
}
|
||
if len(outs) == 1 {
|
||
return "", nil
|
||
}
|
||
outs = outs[:len(outs)-1]
|
||
}
|
||
for i := 0; i < len(outs); i++ {
|
||
out := outs[i]
|
||
switch out.Kind() {
|
||
case reflect.Map:
|
||
if out.IsNil() {
|
||
outs[i] = reflect.MakeMap(out.Type())
|
||
}
|
||
case reflect.Slice:
|
||
if out.IsNil() {
|
||
outs[i] = reflect.MakeSlice(out.Type(), 0, 0)
|
||
}
|
||
}
|
||
}
|
||
if len(outs) == 1 {
|
||
return outs[0].Interface(), nil
|
||
}
|
||
val := make([]any, 0, len(outs))
|
||
for i := range outs {
|
||
val = append(val, outs[i].Interface())
|
||
}
|
||
return val, nil
|
||
}
|
||
|
||
func call(callback open_im_sdk_callback.Base, operationID string, fn any, args ...any) {
|
||
if callback == nil {
|
||
log.ZWarn(context.Background(), "callback is nil", nil)
|
||
return
|
||
}
|
||
go func() {
|
||
res, err := call_(operationID, fn, args...)
|
||
if err != nil {
|
||
if code, ok := err.(errs.CodeError); ok {
|
||
callback.OnError(int32(code.Code()), code.Error())
|
||
} else {
|
||
callback.OnError(sdkerrs.UnknownCode, fmt.Sprintf("error %T not implement CodeError: %s", err, err))
|
||
}
|
||
return
|
||
}
|
||
data, err := json.Marshal(res)
|
||
if err != nil {
|
||
callback.OnError(sdkerrs.SdkInternalError, fmt.Sprintf("function res json.Marshal error: %s", err))
|
||
return
|
||
}
|
||
callback.OnSuccess(string(data))
|
||
}()
|
||
}
|
||
|
||
func syncCall(operationID string, fn any, args ...any) (res string) {
|
||
err := error(nil)
|
||
if operationID == "" {
|
||
return ""
|
||
}
|
||
fnv := reflect.ValueOf(fn)
|
||
if fnv.Kind() != reflect.Func {
|
||
err = errs.ErrRecordNotFound
|
||
return ""
|
||
}
|
||
funcPtr := reflect.ValueOf(fn).Pointer()
|
||
funcName := runtime.FuncForPC(funcPtr).Name()
|
||
if err = CheckResourceLoad(UserForSDK, funcName); err != nil {
|
||
return ""
|
||
}
|
||
fnt := fnv.Type()
|
||
numIn := fnt.NumIn()
|
||
if len(args)+1 != numIn {
|
||
err = errors.New("go code error: fn in args num is not match")
|
||
return ""
|
||
}
|
||
ins := make([]reflect.Value, 0, numIn)
|
||
|
||
ctx := ccontext.WithOperationID(UserForSDK.BaseCtx(), operationID)
|
||
t := time.Now()
|
||
defer func(start time.Time) {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf("panic: %+v\n%s", r, debug.Stack())
|
||
} else {
|
||
elapsed := time.Since(start).Milliseconds()
|
||
if err == nil {
|
||
log.ZInfo(ctx, "fn call success", "function name", funcName, "resp", res, "cost time", fmt.Sprintf("%d ms", elapsed))
|
||
} else {
|
||
log.ZError(ctx, "fn call error", err, "function name", funcName, "cost time", fmt.Sprintf("%d ms", elapsed))
|
||
}
|
||
|
||
}
|
||
}(t)
|
||
log.ZInfo(ctx, "func call req", "function name", funcName, "args", args)
|
||
ins = append(ins, reflect.ValueOf(ctx))
|
||
for i := 0; i < len(args); i++ {
|
||
tag := fnt.In(i + 1)
|
||
arg := reflect.TypeOf(args[i])
|
||
if arg.String() == tag.String() || tag.Kind() == reflect.Interface {
|
||
ins = append(ins, reflect.ValueOf(args[i]))
|
||
continue
|
||
}
|
||
if arg.Kind() == reflect.String { // json
|
||
switch tag.Kind() {
|
||
case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map, reflect.Ptr:
|
||
v := reflect.New(tag)
|
||
if args[i].(string) != "" {
|
||
if err = json.Unmarshal([]byte(args[i].(string)), v.Interface()); err != nil {
|
||
return ""
|
||
}
|
||
}
|
||
|
||
ins = append(ins, v.Elem())
|
||
continue
|
||
}
|
||
}
|
||
if isNumeric(arg.Kind()) && isNumeric(tag.Kind()) {
|
||
v := reflect.Zero(tag).Interface()
|
||
setNumeric(args[i], &v)
|
||
ins = append(ins, reflect.ValueOf(v))
|
||
continue
|
||
}
|
||
err = errors.New("err args type")
|
||
return ""
|
||
}
|
||
var lastErr bool
|
||
if numOut := fnt.NumOut(); numOut > 0 {
|
||
lastErr = fnt.Out(numOut - 1).Implements(reflect.TypeOf((*error)(nil)).Elem())
|
||
}
|
||
outs := fnv.Call(ins)
|
||
if len(outs) == 0 {
|
||
err = errors.New("err res type")
|
||
return ""
|
||
}
|
||
outVals := make([]any, 0, len(outs))
|
||
for i := 0; i < len(outs); i++ {
|
||
outVals = append(outVals, outs[i].Interface())
|
||
}
|
||
if lastErr {
|
||
if last := outVals[len(outVals)-1]; last != nil {
|
||
//callback.OnError(10000, last.(error).Error())
|
||
err = last.(error)
|
||
return ""
|
||
}
|
||
if len(outs) == 1 {
|
||
//callback.OnSuccess("") // 只有一个返回值为error,且error == nil
|
||
return ""
|
||
}
|
||
outVals = outVals[:len(outVals)-1]
|
||
}
|
||
// 将map和slice的nil转换为非nil
|
||
for i := 0; i < len(outVals); i++ {
|
||
switch outs[i].Kind() {
|
||
case reflect.Map:
|
||
if outs[i].IsNil() {
|
||
outVals[i] = reflect.MakeMap(outs[i].Type()).Interface()
|
||
}
|
||
case reflect.Slice:
|
||
if outs[i].IsNil() {
|
||
outVals[i] = reflect.MakeSlice(outs[i].Type(), 0, 0).Interface()
|
||
}
|
||
}
|
||
}
|
||
var jsonVal any
|
||
if len(outVals) == 1 {
|
||
jsonVal = outVals[0]
|
||
} else {
|
||
jsonVal = outVals
|
||
}
|
||
jsonData, err := json.Marshal(jsonVal)
|
||
if err != nil {
|
||
err = errors.New("json marshal error")
|
||
return ""
|
||
}
|
||
return string(jsonData)
|
||
}
|
||
func messageCall(callback open_im_sdk_callback.SendMsgCallBack, operationID string, fn any, args ...any) {
|
||
if callback == nil {
|
||
log.ZWarn(context.Background(), "callback is nil", nil)
|
||
return
|
||
}
|
||
go messageCall_(callback, operationID, fn, args...)
|
||
}
|
||
func messageCall_(callback open_im_sdk_callback.SendMsgCallBack, operationID string, fn any, args ...any) {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Println(" panic err:", r, string(debug.Stack()))
|
||
callback.OnError(sdkerrs.SdkInternalError, fmt.Sprintf("recover: %+v", r))
|
||
return
|
||
}
|
||
}()
|
||
if operationID == "" {
|
||
callback.OnError(sdkerrs.ArgsError, sdkerrs.ErrArgs.WrapMsg("operationID is empty").Error())
|
||
return
|
||
}
|
||
if err := CheckResourceLoad(UserForSDK, ""); err != nil {
|
||
callback.OnError(sdkerrs.ResourceLoadNotCompleteError, "resource load error: "+err.Error())
|
||
return
|
||
}
|
||
fnv := reflect.ValueOf(fn)
|
||
if fnv.Kind() != reflect.Func {
|
||
callback.OnError(sdkerrs.SdkInternalError, "go code error: fn is not function")
|
||
return
|
||
}
|
||
fnt := fnv.Type()
|
||
numIn := fnt.NumIn()
|
||
if len(args)+1 != numIn {
|
||
callback.OnError(sdkerrs.SdkInternalError, "go code error: fn in args num is not match")
|
||
return
|
||
}
|
||
|
||
t := time.Now()
|
||
ins := make([]reflect.Value, 0, numIn)
|
||
ctx := ccontext.WithOperationID(UserForSDK.BaseCtx(), operationID)
|
||
ctx = ccontext.WithSendMessageCallback(ctx, callback)
|
||
funcPtr := reflect.ValueOf(fn).Pointer()
|
||
funcName := runtime.FuncForPC(funcPtr).Name()
|
||
log.ZInfo(ctx, "input req", "function name", funcName, "args", args)
|
||
|
||
ins = append(ins, reflect.ValueOf(ctx))
|
||
for i := 0; i < len(args); i++ { // callback open_im_sdk_callback.Base, operationID string, ...
|
||
tag := fnt.In(i + 1) // ctx context.Context, ...
|
||
arg := reflect.TypeOf(args[i])
|
||
if arg.String() == tag.String() || tag.Kind() == reflect.Interface {
|
||
ins = append(ins, reflect.ValueOf(args[i]))
|
||
continue
|
||
}
|
||
if arg.Kind() == reflect.String { // json
|
||
switch tag.Kind() {
|
||
case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map, reflect.Ptr:
|
||
v := reflect.New(tag)
|
||
if err := json.Unmarshal([]byte(args[i].(string)), v.Interface()); err != nil {
|
||
callback.OnError(sdkerrs.ArgsError, err.Error())
|
||
return
|
||
}
|
||
ins = append(ins, v.Elem())
|
||
continue
|
||
}
|
||
}
|
||
if isNumeric(arg.Kind()) && isNumeric(tag.Kind()) {
|
||
v := reflect.Zero(tag).Interface()
|
||
setNumeric(args[i], &v)
|
||
ins = append(ins, reflect.ValueOf(v))
|
||
continue
|
||
}
|
||
callback.OnError(sdkerrs.ArgsError, "go code error: fn in args type is not match")
|
||
return
|
||
}
|
||
var lastErr bool
|
||
if numOut := fnt.NumOut(); numOut > 0 {
|
||
lastErr = fnt.Out(numOut - 1).Implements(reflect.ValueOf(new(error)).Elem().Type())
|
||
}
|
||
//fmt.Println("fnv:", fnv.Interface(), "ins:", ins)
|
||
outs := fnv.Call(ins)
|
||
|
||
outVals := make([]any, 0, len(outs))
|
||
for i := 0; i < len(outs); i++ {
|
||
outVals = append(outVals, outs[i].Interface())
|
||
}
|
||
if lastErr {
|
||
if last := outVals[len(outVals)-1]; last != nil {
|
||
if code, ok := last.(error).(errs.CodeError); ok {
|
||
callback.OnError(int32(code.Code()), code.Error())
|
||
} else {
|
||
callback.OnError(sdkerrs.UnknownCode, fmt.Sprintf("error %T not implement CodeError: %s", last.(error), last.(error).Error()))
|
||
}
|
||
return
|
||
}
|
||
|
||
outVals = outVals[:len(outVals)-1]
|
||
}
|
||
// 将map和slice的nil转换为非nil
|
||
for i := 0; i < len(outVals); i++ {
|
||
switch outs[i].Kind() {
|
||
case reflect.Map:
|
||
if outs[i].IsNil() {
|
||
outVals[i] = reflect.MakeMap(outs[i].Type()).Interface()
|
||
}
|
||
case reflect.Slice:
|
||
if outs[i].IsNil() {
|
||
outVals[i] = reflect.MakeSlice(outs[i].Type(), 0, 0).Interface()
|
||
}
|
||
}
|
||
}
|
||
var jsonVal any
|
||
if len(outVals) == 1 {
|
||
jsonVal = outVals[0]
|
||
} else {
|
||
jsonVal = outVals
|
||
}
|
||
jsonData, err := json.Marshal(jsonVal)
|
||
if err != nil {
|
||
callback.OnError(sdkerrs.ArgsError, err.Error())
|
||
return
|
||
}
|
||
log.ZInfo(ctx, "output resp", "function name", funcName, "resp", jsonVal, "cost time", time.Since(t))
|
||
callback.OnSuccess(string(jsonData))
|
||
}
|
||
|
||
func listenerCall(fn any, listener any) {
|
||
ctx := context.Background()
|
||
if UserForSDK == nil {
|
||
log.ZWarn(ctx, "UserForSDK is nil,set listener is invalid", nil)
|
||
return
|
||
}
|
||
fnv := reflect.ValueOf(fn)
|
||
if fnv.Kind() != reflect.Func {
|
||
log.ZWarn(ctx, "fn is error,set listener is invalid", nil)
|
||
return
|
||
}
|
||
args := reflect.ValueOf(listener)
|
||
fnv.Call([]reflect.Value{args})
|
||
}
|