text: package GoMybatis
  
  import (
  	"fmt"
  	"github.com/zhuxiujia/GoMybatis/tx"
  	"github.com/zhuxiujia/GoMybatis/utils"
  	"reflect"
  	"strings"
  )
  
  //使用AOP切面 代理目标服务，如果服务painc()它的事务会回滚
  //默认为单协程模型，如果是多协程调用的情况请开启engine.SetGoroutineIDEnable(true)
  func AopProxyService(service interface{}, engine SessionEngine) {
  	var v = reflect.ValueOf(service)
  	if v.Kind() != reflect.Ptr {
  		panic("[GoMybatis] AopProxy service  must use ptr arg!")
  	}
  	AopProxyServiceValue(v, engine)
  }
  
  //使用AOP切面 代理目标服务，如果服务painc()它的事务会回滚
  func AopProxyServiceValue(service reflect.Value, engine SessionEngine) {
  	var beanType = service.Type().Elem()
  	var beanName = beanType.PkgPath() + beanType.Name()
  	ProxyValue(service, func(funcField reflect.StructField, field reflect.Value) func(arg ProxyArg) []reflect.Value {
  		//init data
  		var propagation = tx.PROPAGATION_NEVER
  		var nativeImplFunc = reflect.ValueOf(field.Interface())
  		var txTag, haveTx = funcField.Tag.Lookup("tx")
  		var rollbackTag = funcField.Tag.Get("rollback")
  		if haveTx {
  			propagation = tx.NewPropagation(txTag)
  		}
  		var fn = func(arg ProxyArg) []reflect.Value {
  			var goroutineID int64 //协程id
  			if engine.GoroutineIDEnable() {
  				goroutineID = utils.GoroutineID()
  			} else {
  				goroutineID = 0
  			}
  			var session = engine.GoroutineSessionMap().Get(goroutineID)
  			if session == nil {
  				//todo newSession is use service bean name?
  				var err error
  				session, err = engine.NewSession(beanName)
  				defer func() {
  					if session != nil {
  						session.Close()
  					}
  					engine.GoroutineSessionMap().Delete(goroutineID)
  				}()
  				if err != nil {
  					panic(err)
  				}
  				//压入map
  				engine.GoroutineSessionMap().Put(goroutineID, session)
  			}
  			if !haveTx {
  				var err = session.Begin(session.LastPROPAGATION())
  				if err != nil {
  					panic(err)
  				}
  			}else{
  				var err = session.Begin(&propagation)
  				if err != nil {
  					panic(err)
  				}
  			}
  
  			var nativeImplResult = doNativeMethod(funcField, arg, nativeImplFunc, session, engine.Log())
  			if !haveRollBackType(nativeImplResult, rollbackTag) {
  				var err = session.Commit()
  				if err != nil {
  					panic(err)
  				}
  			} else {
  				var err = session.Rollback()
  				if err != nil {
  					panic(err)
  				}
  			}
  			return nativeImplResult
  		}
  		return fn
  	})
  }
  
  func doNativeMethod(funcField reflect.StructField, arg ProxyArg, nativeImplFunc reflect.Value, session Session, log Log) []reflect.Value {
  	defer func() {
  		err := recover()
  		if err != nil {
  			var rollbackErr = session.Rollback()
  			if rollbackErr != nil {
  				panic(fmt.Sprint(err) + rollbackErr.Error())
  			}
  			if log != nil {
  				log.Println([]byte(fmt.Sprint(err) + " Throw out error will Rollback! from >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> " + funcField.Name + "()"))
  			}
  			panic(err)
  		}
  	}()
  	return nativeImplFunc.Call(arg.Args)
  
  }
  
  func haveRollBackType(v []reflect.Value, typeString string) bool {
  	//println(typeString)
  	if v == nil || len(v) == 0 || typeString == "" {
  		return false
  	}
  	for _, item := range v {
  		if item.Kind() == reflect.Interface {
  			//println(typeString+" == " + item.String())
  			if strings.Contains(item.String(), typeString) {
  				if !item.IsNil() {
  					return true
  				}
  			}
  		}
  	}
  	return false
  }
  