You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
484 lines
14 KiB
484 lines
14 KiB
// 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})
|
|
}
|
|
|