第三方api接口
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.

325 line
7.2 KiB

  1. package comm
  2. import (
  3. "bytes"
  4. "crypto/md5"
  5. "crypto/tls"
  6. "encoding/hex"
  7. "encoding/json"
  8. "errors"
  9. "io"
  10. "io/ioutil"
  11. "net/http"
  12. "net/url"
  13. "sort"
  14. "strconv"
  15. "strings"
  16. "time"
  17. "github.com/bitly/go-simplejson"
  18. "github.com/nilorg/sdk/convert"
  19. )
  20. var (
  21. /**公用方法**/
  22. // Session 用户登录授权成功后,TOP颁发给应用的授权信息。当此API的标签上注明:“需要授权”,则此参数必传;“不需要授权”,则此参数不需要传;“可选授权”,则此参数为可选
  23. Session string
  24. // Timeout ...
  25. Timeout time.Duration
  26. // CacheExpiration 缓存过期时间
  27. CacheExpiration = time.Hour
  28. // GetCache 获取缓存
  29. GetCache getCacheFunc
  30. // SetCache 设置缓存
  31. SetCache setCacheFunc
  32. /**淘宝平台信息**/
  33. TaobaoAppKey string
  34. TaobaoAppSecret string
  35. TaobaoRouter string
  36. TaobaoVersion = "2.0"
  37. /**京东平台信息**/
  38. JDAppKey string
  39. JDAppSecret string
  40. JDRouter string
  41. JDSdkRouter string
  42. JDVersion = "2.0"
  43. /**拼多多平台信息**/
  44. PDDAppKey string
  45. PDDAppSecret string
  46. PDDRouter string
  47. PDDVersion = "v1.0"
  48. /**考拉海购平台信息**/
  49. KaolaAppKey string
  50. KaolaAppSecret string
  51. KaolaRouter string
  52. KaolaVersion = "1.0"
  53. )
  54. // Arg 参数
  55. type Arg map[string]interface{}
  56. // copyArg 复制参数
  57. func copyArg(srcArgs Arg) Arg {
  58. newArgs := make(Arg)
  59. for key, value := range srcArgs {
  60. newArgs[key] = value
  61. }
  62. return newArgs
  63. }
  64. // newCacheKey 创建缓存Key
  65. func newCacheKey(p Arg) string {
  66. cpArgs := copyArg(p)
  67. delete(cpArgs, "session")
  68. delete(cpArgs, "timestamp")
  69. delete(cpArgs, "sign")
  70. keys := []string{}
  71. for k := range cpArgs {
  72. keys = append(keys, k)
  73. }
  74. // 排序asc
  75. sort.Strings(keys)
  76. // 把所有参数名和参数值串在一起
  77. cacheKeyBuf := new(bytes.Buffer)
  78. for _, k := range keys {
  79. cacheKeyBuf.WriteString(k)
  80. cacheKeyBuf.WriteString("=")
  81. cacheKeyBuf.WriteString(interfaceToString(cpArgs[k]))
  82. }
  83. h := md5.New()
  84. io.Copy(h, cacheKeyBuf)
  85. return hex.EncodeToString(h.Sum(nil))
  86. }
  87. // execute 执行API接口
  88. func execute(p Arg, router string) (bytes []byte, err error) {
  89. err = checkConfig()
  90. if err != nil {
  91. return
  92. }
  93. var req *http.Request
  94. req, err = http.NewRequest("POST", router, strings.NewReader(p.getReqData()))
  95. if err != nil {
  96. return
  97. }
  98. tr := &http.Transport{
  99. TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
  100. }
  101. req.Header.Add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
  102. httpClient := &http.Client{Transport: tr}
  103. httpClient.Timeout = Timeout
  104. var resp *http.Response
  105. resp, err = httpClient.Do(req)
  106. if err != nil {
  107. return
  108. }
  109. if resp.StatusCode != 200 {
  110. err = errors.New("请求错误:" + strconv.Itoa(resp.StatusCode))
  111. return
  112. }
  113. defer resp.Body.Close()
  114. bytes, err = ioutil.ReadAll(resp.Body)
  115. // fmt.Printf("\n\n\nreq: %v\n\nresp: %v\n\n\n", p.getReqData(), string(bytes))
  116. return
  117. }
  118. // Execute 执行API接口
  119. func Execute(method string, p Arg) (res *simplejson.Json, err error) {
  120. p, r := setReqData(p, method)
  121. var bodyBytes []byte
  122. bodyBytes, err = execute(p, r)
  123. if err != nil {
  124. return
  125. }
  126. return bytesToResult(bodyBytes)
  127. }
  128. func bytesToResult(bytes []byte) (res *simplejson.Json, err error) {
  129. res, err = simplejson.NewJson(bytes)
  130. if err != nil {
  131. return
  132. }
  133. if responseError, ok := res.CheckGet("error_response"); ok {
  134. if subMsg, subOk := responseError.CheckGet("sub_msg"); subOk {
  135. err = errors.New(subMsg.MustString())
  136. } else if zhDesc, descOk := responseError.CheckGet("zh_desc"); descOk {
  137. err = errors.New(zhDesc.MustString())
  138. } else {
  139. err = errors.New(responseError.Get("msg").MustString())
  140. }
  141. res = nil
  142. }
  143. return
  144. }
  145. // ExecuteCache 执行API接口,缓存
  146. func ExecuteCache(method string, param Arg) (res *simplejson.Json, err error) {
  147. p, r := setReqData(param, method)
  148. cacheKey := newCacheKey(p)
  149. // 获取缓存
  150. if GetCache != nil {
  151. cacheBytes := GetCache(cacheKey)
  152. if len(cacheBytes) > 0 {
  153. res, err = simplejson.NewJson(cacheBytes)
  154. if err == nil && res != nil {
  155. return
  156. }
  157. }
  158. }
  159. var bodyBytes []byte
  160. bodyBytes, err = execute(param, r)
  161. if err != nil {
  162. return
  163. }
  164. res, err = bytesToResult(bodyBytes)
  165. if err != nil {
  166. return
  167. }
  168. ejsonBody, _ := res.MarshalJSON()
  169. // 设置缓存
  170. if SetCache != nil {
  171. go SetCache(cacheKey, ejsonBody, CacheExpiration)
  172. }
  173. return
  174. }
  175. // 检查配置
  176. func checkConfig() error {
  177. if TaobaoAppKey == "" && JDAppKey == "" && KaolaAppKey == "" && PDDAppKey == "" {
  178. return errors.New("至少需要设置一个平台参数")
  179. }
  180. return nil
  181. }
  182. //组装参数及添加公共参数
  183. func setReqData(p Arg, method string) (Arg, string) {
  184. platform := strings.Split(method, ".")[0]
  185. router := ""
  186. hh, _ := time.ParseDuration("8h")
  187. loc := time.Now().UTC().Add(hh)
  188. if platform == "taobao" {
  189. //淘宝
  190. p["timestamp"] = strconv.FormatInt(loc.Unix(), 10)
  191. p["partner_id"] = "Blant"
  192. p["app_key"] = TaobaoAppKey
  193. p["v"] = TaobaoVersion
  194. if Session != "" {
  195. p["session"] = Session
  196. }
  197. p["method"] = method
  198. p["format"] = "json"
  199. p["sign_method"] = "md5"
  200. // 设置签名
  201. p["sign"] = getSign(p, TaobaoAppSecret)
  202. router = TaobaoRouter
  203. } else if platform == "jdSdk" || platform == "jd" {
  204. //京东
  205. param := p
  206. p = Arg{}
  207. p["param_json"] = param
  208. p["app_key"] = JDAppKey
  209. p["v"] = JDVersion
  210. p["timestamp"] = loc.Format("2006-01-02 15:04:05")
  211. p["method"] = method
  212. p["format"] = "json"
  213. p["sign_method"] = "md5"
  214. // 设置签名
  215. p["sign"] = getSign(p, JDAppSecret)
  216. router = JDSdkRouter
  217. if platform == "jd" {
  218. router = JDRouter
  219. }
  220. } else if platform == "pdd" {
  221. //拼多多
  222. p["type"] = method
  223. p["data_type"] = "json"
  224. p["version"] = PDDVersion
  225. p["client_id"] = PDDAppKey
  226. p["timestamp"] = strconv.FormatInt(loc.Unix(), 10)
  227. // 设置签名
  228. p["sign"] = getSign(p, PDDAppSecret)
  229. router = PDDRouter
  230. } else if platform == "kaola" {
  231. //考拉海购
  232. p["method"] = method
  233. p["v"] = KaolaVersion
  234. p["signMethod"] = "md5"
  235. p["unionId"] = KaolaAppKey
  236. p["timestamp"] = loc.Format("2006-01-02 15:04:05")
  237. // 设置签名
  238. p["sign"] = getSign(p, KaolaAppSecret)
  239. router = KaolaRouter
  240. } else if platform == "suning" {
  241. // TODO 苏宁
  242. } else if platform == "vip" {
  243. // TODO 唯品会
  244. }
  245. return p, router
  246. }
  247. // 获取请求数据
  248. func (p Arg) getReqData() string {
  249. // 公共参数
  250. args := url.Values{}
  251. // 请求参数
  252. for key, val := range p {
  253. args.Set(key, interfaceToString(val))
  254. }
  255. return args.Encode()
  256. }
  257. // 获取签名
  258. func getSign(p Arg, secret string) string {
  259. // 获取Key
  260. var keys []string
  261. for k := range p {
  262. keys = append(keys, k)
  263. }
  264. // 排序asc
  265. sort.Strings(keys)
  266. // 把所有参数名和参数值串在一起
  267. query := bytes.NewBufferString(secret)
  268. for _, k := range keys {
  269. query.WriteString(k)
  270. query.WriteString(interfaceToString(p[k]))
  271. }
  272. query.WriteString(secret)
  273. // 使用MD5加密
  274. h := md5.New()
  275. _, _ = io.Copy(h, query)
  276. // 把二进制转化为大写的十六进制
  277. return strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
  278. }
  279. func interfaceToString(src interface{}) string {
  280. if src == nil {
  281. panic(ErrTypeIsNil)
  282. }
  283. switch src.(type) {
  284. case string:
  285. return src.(string)
  286. case int, int8, int32, int64:
  287. case uint8, uint16, uint32, uint64:
  288. case float32, float64:
  289. return convert.ToString(src)
  290. }
  291. data, err := json.Marshal(src)
  292. if err != nil {
  293. panic(err)
  294. }
  295. return string(data)
  296. }