using Api.Framework.Model; using CsharpHttpHelper; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Security.Cryptography; using System.Text; using System.Threading; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Api.Framework.Cps { /// /// 拼多多操作Api /// public class PinduoduoApi : BaseCpsApi { #region 静态 /// /// 拼多多接口请求地址 /// private const string app_host = "http://gw-api.pinduoduo.com/api/router"; /// /// 计算sign /// /// 参数 /// appkey /// private string SignTopRequest(Dictionary parameters, string appkey) { // 第一步:把字典按Key的字母顺序排序 IDictionary sortedParams = new SortedDictionary(parameters, StringComparer.Ordinal); // 第二步:把所有参数名和参数值串在一起 StringBuilder query = new StringBuilder(); query.Append(appkey); foreach (KeyValuePair kv in sortedParams) { if (!string.IsNullOrEmpty(kv.Key) && !string.IsNullOrEmpty(kv.Value)) { query.Append(kv.Key).Append(kv.Value); } } // 第四步 byte[] bytes; query.Append(appkey); MD5 md5 = MD5.Create(); bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(query.ToString())); // 第五步:把二进制转化为大写的十六进制 StringBuilder result = new StringBuilder(); for (int i = 0; i < bytes.Length; i++) { result.Append(bytes[i].ToString("X2")); } return result.ToString(); } #endregion #region 实际操作 /// /// 构造方法 /// /// internal PinduoduoApi(fl_cps_member member) : base(member) { } /// /// 刷新授权 /// internal void RefreshStatus() { for (int i = 0; i < 3; i++) { try { //if (Member.cpstype == Enums.CpsType.多多进宝) // Console.WriteLine(); //UpdateCookies(); var result = SendServer("refresh_token", new { username = Member.username }); if (!result.ok) throw new Exception(result.message.ToString()); Member.online = true; RefToken(); break; } catch (Exception ex) { if (i < 2) { Thread.Sleep(10000); continue; } else EventClient.OnEvent(this, $"ERROR:{this.GetType()}@{System.Reflection.MethodBase.GetCurrentMethod().Name}->{ex.Message}"); } } } /// /// 发送拼多多报文 /// /// 请求的api /// 参数 /// public Dictionary SendPinduoduo(string api, object data) { int number = 0; string html = string.Empty; Next: try { number++; //throw new Exception("{\"error_msg\":\"公共参数错误: access_token\",\"sub_msg\":\"公共参数错误: access_token\",\"sub_code\":\"10019\",\"error_code\":10019,\"request_id\":\"16249438515389810\"}"); Dictionary param = new Dictionary(); var type = data.GetType().GetProperties(); foreach (var item in type) { var _value = item.GetValue(data).ToString(); param[item.Name] = _value; } //if (api != "pdd.ddk.oauth.order.list.increment.get" && api != "pdd.ddk.oauth.goods.search") if (api.Contains("oauth")) param["access_token"] = Token.access_token; param["type"] = api; param["client_id"] = Token.appid; //param["timestamp"] = ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000).ToString();//HttpExtend.GetTimeStamp(DateTime.Now).ToString(); param["timestamp"] = HttpExtend.GetTimeStamp(); param["data_type"] = "JSON"; var sign = SignTopRequest(param, Token.appkey); param["sign"] = sign; HttpHelper http = new HttpHelper(); HttpItem _item = new HttpItem() { URL = app_host, Method = "Post",//URL 可选项 默认为Get Timeout = 10000,//连接超时时间 可选项默认为100000 ReadWriteTimeout = 10000,//写入Post数据超时时间 可选项默认为30000 IsToLower = false,//得到的HTML代码是否转成小写 可选项默认转小写 Cookie = "",//字符串Cookie 可选项 UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0",//用户的浏览器类型,版本,操作系统 可选项有默认值 Accept = "text/html, application/xhtml+xml, */*",// 可选项有默认值 ContentType = "application/x-www-form-urlencoded",//返回类型 可选项有默认值 Referer = "",//来源URL 可选项 Postdata = HttpExtend.BuildQuery(param),//Post数据 可选项GET时不需要写 }; html = http.GetHtml(_item).Html; //if (html.Contains("access_token已过期")) Member.online = false; //else if (api == "pdd.ddk.oauth.order.list.increment.get") Member.online = true; var dic = HttpExtend.JsonToDictionary(html); if (dic.ContainsKey("error_response")) { //EventClient.OnEvent(this, $"拼多多接口异常:{this.Member.username}({this.Member.usernick})->{api}->{html}"); throw new Exception(HttpHelper.ObjectToJson(dic["error_response"])); } return dic; } catch (Exception ex) { if (ex.Message.Contains("access_token") && ((ex.Message.Contains("10019") || ex.Message.Contains("20033") || ex.Message.Contains("20032")))) { Thread.Sleep(1000); if (this.Member != null && RefToken() && this.Member.is_valid && number < 3) goto Next; else { //Member.online = false; //ApiClient.GetSession().SaveOrUpdate(Member); Member.is_valid = false; ApiClient.GetSession().SaveOrUpdate(Member); EventClient.OnEvent(this, $@"拼多多异常:{this.Member.username}({this.Member.usernick}),{ex.Message} - {ex.StackTrace} ,注:请重新登录拼多多,{api}"); throw new Exception($"{this.Member.username}({this.Member.usernick}),{ex.Message},注:请重新登录拼多多"); } } else if (html.Contains("超时")) { goto Next; } throw ex; } } /// /// 发送拼多多报文 /// /// 请求的api /// 参数 /// public JToken SendPinduoduoJToken(string api, object data) { int number = 0; string html = string.Empty; Next: try { number++; //throw new Exception("{\"error_msg\":\"公共参数错误: access_token\",\"sub_msg\":\"公共参数错误: access_token\",\"sub_code\":\"10019\",\"error_code\":10019,\"request_id\":\"16249438515389810\"}"); Dictionary param = new Dictionary(); var type = data.GetType().GetProperties(); foreach (var item in type) { var _value = item.GetValue(data).ToString(); param[item.Name] = _value; } //if (api != "pdd.ddk.oauth.order.list.increment.get" && api != "pdd.ddk.oauth.goods.search") if (api.Contains("oauth")) param["access_token"] = Token.access_token; param["type"] = api; param["client_id"] = Token.appid; //param["timestamp"] = ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000).ToString();//HttpExtend.GetTimeStamp(DateTime.Now).ToString(); param["timestamp"] = HttpExtend.GetTimeStamp(); param["data_type"] = "JSON"; var sign = SignTopRequest(param, Token.appkey); param["sign"] = sign; HttpHelper http = new HttpHelper(); HttpItem _item = new HttpItem() { URL = app_host, Method = "Post",//URL 可选项 默认为Get Timeout = 10000,//连接超时时间 可选项默认为100000 ReadWriteTimeout = 10000,//写入Post数据超时时间 可选项默认为30000 IsToLower = false,//得到的HTML代码是否转成小写 可选项默认转小写 Cookie = "",//字符串Cookie 可选项 UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0",//用户的浏览器类型,版本,操作系统 可选项有默认值 Accept = "text/html, application/xhtml+xml, */*",// 可选项有默认值 ContentType = "application/x-www-form-urlencoded",//返回类型 可选项有默认值 Referer = "",//来源URL 可选项 Postdata = HttpExtend.BuildQuery(param),//Post数据 可选项GET时不需要写 }; var result = http.GetHtml(_item); //if (html.Contains("access_token已过期")) Member.online = false; //else if (api == "pdd.ddk.oauth.order.list.increment.get") Member.online = true; if (result.StatusCode == HttpStatusCode.OK) { html = result.Html; JObject jObj = null; try { jObj = JObject.Parse(html); } catch (Exception e) { return null; } if (jObj?.First?.First != null) { var jData = jObj.First.First; if (jData["error_code"] != null) //throw new Exception(jObj.ToString()); throw new Exception(jData["error_msg"] + " 参数:" + JsonConvert.SerializeObject(data)); return jData; } } return null; } catch (Exception ex) { if (ex.Message.Contains("access_token") && ((ex.Message.Contains("10019") || ex.Message.Contains("20033") || ex.Message.Contains("20032")))) { Thread.Sleep(1000); if (this.Member != null && RefToken() && this.Member.is_valid && number < 3) goto Next; else { //Member.online = false; //ApiClient.GetSession().SaveOrUpdate(Member); Member.is_valid = false; ApiClient.GetSession().SaveOrUpdate(Member); EventClient.OnEvent(this, $@"拼多多异常:{this.Member.username}({this.Member.usernick}),{ex.Message} - {ex.StackTrace} ,注:请重新登录拼多多,{api}"); throw new Exception($"{this.Member.username}({this.Member.usernick}),{ex.Message},注:请重新登录拼多多"); } } else if (html.Contains("超时")) { goto Next; } throw ex; } } /// /// 拼多多下载订单 /// /// 开始时间 /// 结束时间 /// 每页数量默认50安全 /// 第几页 /// internal Dictionary DownOrder(DateTime start_time, DateTime end_time, int page_index, int page_size) { if (!Member.is_valid) throw new Exception("需要重新登录"); var _rst = this.SendPinduoduo("pdd.ddk.oauth.order.list.increment.get", new { start_update_time = HttpExtend.GetTimeStamp(start_time), end_update_time = HttpExtend.GetTimeStamp(end_time), page_size = page_size, page = page_index }); if (_rst.ContainsKey("order_list_get_response")) { return _rst["order_list_get_response"] as Dictionary; } return null; } #endregion /// /// 生成授权地址 /// /// /// public string AuthorityUrl(string pid) { //授权的信息 var result = SendPinduoduoJToken("pdd.ddk.oauth.rp.prom.url.generate", new { generate_short_url = true, p_id_list = "[\"" + pid + "\"]", channel_type = 10 }); var urlList = result["url_list"]?.ToArray(); if (urlList == null || urlList.Length <= 0) { return string.Empty; } var mobileUrl = urlList[0]?["mobile_short_url"]?.ToString(); return mobileUrl; } /// /// 用户是否授权 /// /// /// public bool IsAuthority(string adzoneAdzonePid) { var jToken = SendPinduoduoJToken("pdd.ddk.oauth.member.authority.query", new { pid = adzoneAdzonePid, custom_parameters = "" }); return jToken["bind"]?.Value() == 1; } } }