// Copyright 2019 getensh.com. All rights reserved. // Use of this source code is governed by getensh.com. package base_api import ( "context" "encoding/hex" "encoding/json" "fmt" "gd_management/apis" "gd_management/common.in/config" "gd_management/common.in/httpClient" "gd_management/common.in/utils" "gd_management/consts" "gd_management/errors" "github.com/astaxie/beego/orm" "github.com/tidwall/gjson" "strconv" "strings" "sync" ) // convertParam 将value转为ptype指定的数据类型, 返回值依次为转换后的数据,是否是struct nil, 错误信息 func convertParam(value string, ptype string, child []apis.ManagementBaseApiParam) (interface{}, bool, error) { ptype = strings.ToLower(ptype) if strings.Contains(ptype, "int") { v, _ := strconv.Atoi(value) return v, false, nil } if strings.Contains(ptype, "float") { v, _ := strconv.ParseFloat(value, 64) return v, false, nil } if strings.Contains(ptype, "bool") { if value == "true" { return true, false, nil } return false, false, nil } if strings.Contains(ptype, "list") { v := []interface{}{} if value == "" { return nil, true, nil } err := json.Unmarshal([]byte(value), &v) if err != nil { return nil, false, errors.ArgsError } return v, false, nil } if ptype == "struct" { if len(child) == 0 { return nil, true, nil } v, err := parseStructParam(child) if err != nil { return nil, false, err } return v, false, nil } return value, false, nil } func encrypt(src string, secret string,encryptType int,head map[string]string) []byte { var retMap = map[string]string{} json.Unmarshal([]byte(src),&retMap) // AES if encryptType == consts.AESCRYPTO{ for k,v := range retMap{ bytes, _ := config.AesEncrypt(v, secret) ret := hex.EncodeToString(bytes) retMap[k] = ret } head["encrypt_type"] = "AES" // MD5 }else if encryptType == 3{ //head["encrypt_type"] = "MD5" } rByte ,_ :=json.Marshal(retMap) /*bytes, _ := config.AesEncrypt(src, secret) ret := hex.EncodeToString(bytes) return ret*/ return rByte } func decrypt(src string, secret string) string { if src == "" { return "" } bytes, err := hex.DecodeString(src) if err != nil { return src } ret, err := config.AesDecrypt(bytes, []byte(secret)) if err != nil { return src } return string(ret) } // parseStructParam 解析结构体数据 func parseStructParam(params []apis.ManagementBaseApiParam) (map[string]interface{}, error) { var m = map[string]interface{}{} for _, v := range params { value := strings.Replace(v.Value, "\r\n", "", -1) realValue, isStructNil, err := convertParam(value, v.Type, v.Child) if err != nil { return nil, err } if isStructNil == false { if v.In != "" { m[v.In] = realValue } else { m[v.Name] = realValue } } } return m, nil } // parseParam 解析数据查询入参为json字节流 func parseParam(req *apis.ManagementTryBaseApiReq) ([]byte, error) { var m = map[string]interface{}{} if len(req.Params) == 0 { return nil, nil } m, err := parseStructParam(req.Params) if err != nil { return nil, err } bytes, _ := json.Marshal(m) return bytes, nil } func getSign(ts string, key string, secret string, param string) string { signtext := fmt.Sprintf("%s%s%s%s", ts, param, key, secret) signcomput := utils.MD5(signtext) return signcomput } type ApiResponse struct { Code int `json:"code" description:"返回码"` Msg string `json:"msg" description:"错误消息" default:""` Data interface{} `json:"data"` OrderNo string `json:"order_no,omitempty"` } // getUrl 组装api url func getUrl(host string, router string) string { return strings.TrimRight(host, "/") + "/" + strings.TrimPrefix(router, "/") } func getToken(user, password string) (string, error) { url := getUrl(config.Conf.GdApiHost, "/api/v1/token") apiParam := map[string]string{ "user": user, "password": password, } resp, err := httpClient.HttpGetWithHead(url, map[string]string{}, apiParam) // 响应结果处理 if err != nil { return "", err } token := gjson.GetBytes(resp, "token").String() if token == "" { return "", errors.LoginTokenFail } return token, nil } // ManagementTryBaseApi 数据查询接口 func ManagementTryBaseApi(ctx context.Context, req *apis.ManagementTryBaseApiReq, reply *apis.ManagementTryBaseApiReply) (err error) { //ts := fmt.Sprintf("%d", time.Now().Unix()) // 参数转换 param, err := parseParam(req) if err != nil { return err } // 通用参数检查 if req.Method == "" { return errors.ArgsError } if config.Conf.GdApiHost == "" { return errors.EtcdError } if req.AppSecret == "" || req.AppKey == "" || req.AppPassword == "" { return errors.ArgsError } token, err := getToken(req.AppKey, req.AppPassword) if err != nil { reply.Data = err.Error() if strings.Contains(reply.Data, "\"code\"") == false { m := map[string]interface{}{ "code": 10002, "msg": reply.Data, "data": "", } bytes, _ := json.Marshal(m) reply.Data = string(bytes) } return nil } // url, 签名,header 准备 url := getUrl(config.Conf.GdApiHost, req.Router) //appKey := req.AppKey appSecret := req.AppSecret method := strings.ToUpper(req.Method) //en := string(param) head := map[string]string{ "token": token, } param = encrypt(string(param), appSecret,req.EncryptoType,head) //sign := getSign(ts, appKey, appSecret, en) if !req.MerchantAccount { if config.Conf.GdInnerAccount == "" { return errors.EtcdError } head["replace_app_key"] = config.Conf.GdInnerAccount } apiParam := map[string]string{} m := map[string]interface{}{} json.Unmarshal(param, &m) for k, v := range m { apiParam[k] = fmt.Sprintf("%v", v) } // 调用api var resp []byte if method == "GET" { resp, err = httpClient.HttpGetWithHead(url, head, apiParam) } if method == "DELETE" { resp, err = httpClient.HttpDeleteWithHead(url, head, apiParam) } if method == "POST" { bytes, _ := json.Marshal(apiParam) resp, err = httpClient.HttpPostWithHead(url, head, nil, bytes) } if method == "PUT" { bytes, _ := json.Marshal(apiParam) resp, err = httpClient.HttpPutWithHead(url, head, nil, bytes) } // 响应结果处理 if err != nil { reply.Data = err.Error() if strings.Contains(reply.Data, "\"code\"") == false { m := map[string]interface{}{ "code": 10002, "msg": reply.Data, "data": "", } bytes, _ := json.Marshal(m) reply.Data = string(bytes) } return nil } if len(resp) > 0 { ret := ApiResponse{} err := json.Unmarshal(resp, &ret) if err != nil { reply.Data = string(resp) return nil } if ret.Data == nil{ ret.Data = "" } text := "" // AES加密 if req.EncryptoType == consts.AESCRYPTO { text = decrypt(ret.Data.(string), appSecret) } else { tmpbytes, _ := json.Marshal(ret.Data) text = string(tmpbytes) } ret.Data = text bytes, _ := json.Marshal(ret) reply.Data = string(bytes) reply.OrderNo = ret.OrderNo } return err } // convertType 将字符串数据value,转化为ptype指定的类型 func convertType(ptype string, value string) interface{} { ptype = strings.ToLower(ptype) if ptype == "" || strings.Contains(ptype, "string") { return value } if strings.Contains(ptype, "bool") { if value == "0" || value == "false" || value == "FALSE" { return false } return true } if strings.Contains(ptype, "int") && strings.Contains(ptype, "[") == false { ret, _ := strconv.Atoi(value) return ret } if strings.Contains(ptype, "float") && strings.Contains(ptype, "[") == false { ret, _ := strconv.ParseFloat(value, 64) return ret } return value } // batchCallApi 跑批调用api func batchCallApi(req *apis.ManagementBatchTryApiReq, m map[string]interface{}) ([]byte, error) { //ts := fmt.Sprintf("%d", time.Now().Unix()) param, _ := json.Marshal(m) //config.Conf.GdApiHost = "http://192.168.1.40:61001" if config.Conf.GdApiHost == "" { return nil, errors.EtcdError } if req.AppKey == "" || req.AppSecret == "" || req.AppPassword == "" { return nil, errors.ArgsError } token, err := getToken(req.AppKey, req.AppPassword) if err != nil { return nil, err } url := getUrl(config.Conf.GdApiHost, req.Router) //appKey := req.AppKey appSecret := req.AppSecret method := strings.ToUpper(req.Method) //en := string(param) head := map[string]string{ //"app_key": appKey, //: ts, //"sign": sign, "token": token, } param = encrypt(string(param), appSecret,req.EncryptoType,head) //sign := getSign(ts, appKey, appSecret, en) if !req.MerchantAccount { if config.Conf.GdInnerAccount == "" { return nil, errors.EtcdError } head["replace_app_key"] = config.Conf.GdInnerAccount } apiParam := map[string]string{} m1 := map[string]interface{}{} json.Unmarshal(param, &m1) for k, v := range m1 { apiParam[k] = fmt.Sprintf("%v", v) } if method == "GET" { return httpClient.HttpGetWithHead(url, head, apiParam) } if method == "DELETE" { return httpClient.HttpDeleteWithHead(url, head, apiParam) } if method == "POST" { bytes, _ := json.Marshal(apiParam) return httpClient.HttpPostWithHead(url, head, nil, bytes) } if method == "PUT" { bytes, _ := json.Marshal(apiParam) return httpClient.HttpPutWithHead(url, head, nil, bytes) } return nil, nil } // parseBatchAndCallMultiMode 跑批并发调接口 func parseBatchAndCallMultiMode(reqstring string, req *apis.ManagementBatchTryApiReq, reply *apis.ManagementBatchTryApiReply) (stop bool) { reqstruct := []apis.ManagementBaseApiParam{} json.Unmarshal([]byte(reqstring), &reqstruct) var wg sync.WaitGroup wg.Add(len(req.Params)) ret := make([]apis.BatchTryApiParams, len(req.Params)) var mu sync.Mutex for i, pa := range req.Params { // index 为参数顺序,并发执行后响应结果需和请求参数顺序一致 go func(index int, v string) { // 准备参数 data := map[string]interface{}{} m := map[string]string{} json.Unmarshal([]byte(v), &m) for _, p := range reqstruct { if value, ok := m[p.Name]; ok { data[p.Name] = convertType(p.Type, value) } } resp, err := batchCallApi(req, data) item := apis.BatchTryApiParams{} item.Requset = v if err != nil { item.OriginResponse = err.Error() } else { ret := ApiResponse{} err := json.Unmarshal(resp, &ret) if err != nil { item.OriginResponse = string(resp) } else { // 通用错误表示后续数据不续再传递 if ret.Code == 10019 || ret.Code == 10016 || ret.Code == 10017 || (ret.Code >= 10004 && ret.Code <= 10013) { mu.Lock() stop = true mu.Unlock() } appSecret := req.AppSecret text := "" if req.IsCrypto { text = decrypt(ret.Data.(string), appSecret) } else { tmp, _ := json.Marshal(ret.Data) text = string(tmp) } ret.Data = text bytes, _ := json.Marshal(ret) item.Response = string(bytes) } } reply.Params = append(reply.Params, item) ret[index] = item wg.Done() }(i, pa) } wg.Wait() reply.Params = ret[:] return } // parseBatchAndCall 跑批顺序调接口 func parseBatchAndCall(reqstring string, req *apis.ManagementBatchTryApiReq, reply *apis.ManagementBatchTryApiReply) (stop bool) { reqstruct := []apis.ManagementBaseApiParam{} json.Unmarshal([]byte(reqstring), &reqstruct) for _, v := range req.Params { // 准备参数 data := map[string]interface{}{} m := map[string]string{} json.Unmarshal([]byte(v), &m) for _, p := range reqstruct { if value, ok := m[p.Name]; ok { data[p.Name] = convertType(p.Type, value) } } // 调用接口 resp, err := batchCallApi(req, data) item := apis.BatchTryApiParams{} item.Requset = v // 响应结果处理 if err != nil { item.OriginResponse = err.Error() } else { ret := ApiResponse{} err := json.Unmarshal(resp, &ret) if err != nil { item.OriginResponse = string(resp) } else { // 通用错误表示后续数据不续再传递 if ret.Code == 10019 || ret.Code == 10016 || ret.Code == 10017 || (ret.Code >= 10004 && ret.Code <= 10013) { stop = true } // 响应参数解密 appSecret := req.AppSecret text := "" if req.IsCrypto { text = decrypt(ret.Data.(string), appSecret) } else { tmp, _ := json.Marshal(ret.Data) text = string(tmp) } ret.Data = text bytes, _ := json.Marshal(ret) item.Response = string(bytes) } //item.Response = string(resp) } reply.Params = append(reply.Params, item) } return stop } // ManagementBatchTryApi 数据跑批接口 func ManagementBatchTryApi(ctx context.Context, req *apis.ManagementBatchTryApiReq, reply *apis.ManagementBatchTryApiReply) (err error) { o := orm.NewOrm() req.Router = strings.TrimPrefix(req.Router, "/") req.Method = strings.ToUpper(req.Method) sql := "select request_param from t_gd_api where router =? and method=?" reqstring := "" err = o.Raw(sql, "/"+req.Router, req.Method).QueryRow(&reqstring) if err != nil { return errors.DataBaseError } if req.MultiMode { reply.Stop = parseBatchAndCallMultiMode(reqstring, req, reply) } else { reply.Stop = parseBatchAndCall(reqstring, req, reply) } return nil }