2022-09-20 03:10:29 +00:00
using Api.Framework.Model ;
using CsharpHttpHelper ;
using System ;
using System.Collections.Generic ;
2022-11-29 02:57:27 +00:00
using System.Linq ;
2023-04-12 09:06:04 +00:00
using System.Net ;
2022-09-20 03:10:29 +00:00
using System.Security.Cryptography ;
using System.Text ;
using System.Threading ;
2023-04-12 09:06:04 +00:00
using Newtonsoft.Json ;
using Newtonsoft.Json.Linq ;
2022-09-20 03:10:29 +00:00
namespace Api.Framework.Cps
{
/// <summary>
/// 拼多多操作Api
/// </summary>
public class PinduoduoApi : BaseCpsApi
{
#region 静 态
/// <summary>
/// 拼多多接口请求地址
/// </summary>
private const string app_host = "http://gw-api.pinduoduo.com/api/router" ;
/// <summary>
/// 计算sign
/// </summary>
/// <param name="parameters">参数</param>
/// <param name="appkey">appkey</param>
/// <returns></returns>
private string SignTopRequest ( Dictionary < string , string > parameters , string appkey )
{
// 第一步: 把字典按Key的字母顺序排序
IDictionary < string , string > sortedParams = new SortedDictionary < string , string > ( parameters , StringComparer . Ordinal ) ;
// 第二步:把所有参数名和参数值串在一起
StringBuilder query = new StringBuilder ( ) ;
query . Append ( appkey ) ;
foreach ( KeyValuePair < string , string > 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 实 际 操 作
/// <summary>
/// 构造方法
/// </summary>
/// <param name="member"></param>
internal PinduoduoApi ( fl_cps_member member ) : base ( member ) { }
/// <summary>
/// 刷新授权
/// </summary>
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}" ) ;
}
}
}
/// <summary>
/// 发送拼多多报文
/// </summary>
/// <param name="api">请求的api</param>
/// <param name="data">参数</param>
/// <returns></returns>
public Dictionary < string , object > 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 < string , string > param = new Dictionary < string , string > ( ) ;
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 ;
2023-04-12 09:06:04 +00:00
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 ;
}
}
/// <summary>
/// 发送拼多多报文
/// </summary>
/// <param name="api">请求的api</param>
/// <param name="data">参数</param>
/// <returns></returns>
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 < string , string > param = new Dictionary < string , string > ( ) ;
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 ) ;
2022-09-20 03:10:29 +00:00
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 ;
}
}
/// <summary>
/// 拼多多下载订单
/// </summary>
/// <param name="start_time">开始时间</param>
/// <param name="end_time">结束时间</param>
/// <param name="page_index">每页数量默认50安全</param>
/// <param name="page_size">第几页</param>
/// <returns></returns>
internal Dictionary < string , object > 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 < string , object > ;
}
return null ;
}
#endregion
2023-04-12 09:06:04 +00:00
/// <summary>
/// 生成授权地址
/// </summary>
/// <param name="pid"></param>
/// <returns></returns>
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 ;
}
/// <summary>
/// 用户是否授权
/// </summary>
/// <param name="adzoneAdzonePid"></param>
/// <returns></returns>
public bool IsAuthority ( string adzoneAdzonePid )
{
var jToken = SendPinduoduoJToken ( "pdd.ddk.oauth.member.authority.query" , new { pid = adzoneAdzonePid , custom_parameters = "" } ) ;
return jToken [ "bind" ] ? . Value < int > ( ) = = 1 ;
}
2022-09-20 03:10:29 +00:00
}
}