2024-06-24 17:48:33 +08:00

485 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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})
}