407 lines
16 KiB
C#
407 lines
16 KiB
C#
using Api.Framework.Model;
|
||
using Api.Framework.Tools;
|
||
using CsharpHttpHelper;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace Api.Framework.Cps
|
||
{
|
||
/// <summary>
|
||
/// 京东操作Api
|
||
/// </summary>
|
||
public class JingdongApi : BaseCpsApi
|
||
{
|
||
#region 固定函数
|
||
/// <summary>
|
||
/// 构造方法
|
||
/// </summary>
|
||
/// <param name="member">fl_cps_member实例</param>
|
||
public JingdongApi(fl_cps_member member) : base(member) { }
|
||
|
||
/// <summary>
|
||
/// 刷新授权
|
||
/// </summary>
|
||
public void RefreshStatus()
|
||
{
|
||
try
|
||
{
|
||
var result = SendServer("refresh_token", new { username = Member.username });
|
||
if (!result.ok) throw new Exception(result.message.ToString());
|
||
Member.online = true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
EventClient.OnEvent(this, $"ERROR:{this.GetType()}-{System.Reflection.MethodBase.GetCurrentMethod().Name}->{ex.Message}");
|
||
}
|
||
}
|
||
|
||
#region
|
||
private static int AppKeysIndex = -1;
|
||
private static string[] AppKeys = null;
|
||
|
||
private static List<string> WhiteApiList = new List<string>() {
|
||
"jd.union.open.promotion.byunionid.get"/*转链*/,
|
||
"jd.union.open.position.query"/*推广位*/,
|
||
"jd.union.open.position.create"/*创建推广位*/,
|
||
"jd.union.open.goods.jingfen.query"/*京粉精选商品*/,
|
||
"jd.union.open.goods.promotiongoodsinfo.query"/*根据skuid查询商品信息*/ };
|
||
|
||
|
||
private static string[] KeyInfo = null;
|
||
private static DateTime UpdateKeyTime = DateTime.MinValue;
|
||
private string[] GetKeySecret()
|
||
{
|
||
if (UpdateKeyTime.AddDays(1) < DateTime.Now)
|
||
KeyInfo = null;
|
||
if (KeyInfo == null)
|
||
{
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
try
|
||
{
|
||
var data = this.SendServer("find_wangzhi", "webTool.asmx", new { name = "京东APPKEY" }).message.ToString();
|
||
if (!string.IsNullOrWhiteSpace(data))
|
||
{
|
||
/*
|
||
appkey="4363c9848d8a43c8b7b9e957d3adcbcf";
|
||
"474ae90f6930495988c28701b6be1d9a";
|
||
*/
|
||
//data = "4363c9848d8a43c8b7b9e957d3adcbcf_474ae90f6930495988c28701b6be1d9a";
|
||
KeyInfo = data.Split(new string[] { "_" }, StringSplitOptions.RemoveEmptyEntries);
|
||
break;
|
||
}
|
||
continue;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
KeyInfo = null;
|
||
Thread.Sleep(500);
|
||
}
|
||
}
|
||
UpdateKeyTime = DateTime.Now;
|
||
}
|
||
return KeyInfo;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送京东报文
|
||
/// </summary>
|
||
/// <param name="api">api接口</param>
|
||
/// <param name="data">参数</param>
|
||
/// <returns></returns>
|
||
public Dictionary<string, object> SendJingdong(string api, object data)
|
||
{
|
||
try
|
||
{
|
||
//执行次数
|
||
int num = 1;
|
||
Next:
|
||
string[] appKey = null;
|
||
if (AppKeysIndex == -1)
|
||
{
|
||
//appkey = "4363c9848d8a43c8b7b9e957d3adcbcf";
|
||
//secretkey = "474ae90f6930495988c28701b6be1d9a";
|
||
|
||
if (WhiteApiList.Contains(api))
|
||
//appKey = new string[] { "dd09c846450c044cf4977357e25ef2cb", "8a610e949a0d4657b310def702728059" };//自己的目前30W
|
||
appKey = GetKeySecret();//自己的目前30W
|
||
if (appKey == null)
|
||
appKey = new string[] { "4363c9848d8a43c8b7b9e957d3adcbcf", "474ae90f6930495988c28701b6be1d9a" };//别人的,无限制
|
||
}
|
||
//else
|
||
//{
|
||
// //第一次
|
||
// if (AppKeys == null || AppKeysIndex >= AppKeys.Length)
|
||
// {
|
||
// try
|
||
// {
|
||
// if (AppKeys != null && AppKeysIndex >= AppKeys.Length) AppKeysIndex = -1;
|
||
// else if (AppKeys == null && AppKeysIndex != -1) AppKeysIndex = -1;
|
||
|
||
// var result = base.SendServer("get_appkey", "");
|
||
// var msg = result.message.ToString();
|
||
// msg = ZipHelper.GZipDecompressString(msg);
|
||
// AppKeys = msg.Split(',');
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// throw new Exception("获取京东授权KEY失败!" + ex.Message);
|
||
// }
|
||
// }
|
||
// appKey = AppKeys[AppKeysIndex].Split('_');
|
||
//}
|
||
|
||
Dictionary<string, string> param = new Dictionary<string, string>();
|
||
|
||
param["access_token"] = "";
|
||
param["app_key"] = appKey[0];
|
||
param["format"] = "json";
|
||
param["method"] = api;
|
||
param["param_json"] = CsharpHttpHelper.HttpHelper.ObjectToJson(data);//param_json为空的时候需要写成 "{}"
|
||
param["sign_method"] = "md5";
|
||
param["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||
param["v"] = "1.0";
|
||
|
||
//计算签名
|
||
var sign = SignTopRequest(param, appKey[1]);
|
||
param["sign"] = sign;
|
||
|
||
if (param.ContainsKey("param_json"))
|
||
{
|
||
param["param_json"] = HttpHelper.URLEncode(param["param_json"]);
|
||
}
|
||
|
||
var _param = HttpHelper.URLDecode(HttpExtend.BuildQuery(param));
|
||
HttpHelper http = new HttpHelper();
|
||
HttpItem _item = new HttpItem()
|
||
{
|
||
URL = "https://router.jd.com/api?" + _param,
|
||
Method = "get",
|
||
Timeout = 10000,
|
||
ReadWriteTimeout = 10000,
|
||
IsToLower = false,
|
||
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 = ""
|
||
};
|
||
|
||
Dictionary<string, object> dic = null;
|
||
string html = string.Empty;
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
html = http.GetHtml(_item).Html;
|
||
if (html.Contains("操作超时") || html.Contains("服务端异常"))
|
||
{
|
||
Thread.Sleep(200);
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
dic = HttpExtend.JsonToDictionary(html);
|
||
if (dic == null) continue;
|
||
}
|
||
break;
|
||
}
|
||
if (html.Contains("操作超时") || html.Contains("服务端异常")) return new Dictionary<string, object>();
|
||
if (dic == null)
|
||
{
|
||
EventClient.OnEvent(this, $"【京东】 => 请求失败转换异常:{html}");
|
||
return new Dictionary<string, object>();
|
||
}
|
||
var response = api.Replace(".", "_") + "_response";
|
||
if (dic.ContainsKey(response))
|
||
{
|
||
var result = dic[response] as Dictionary<string, object>;
|
||
if (result.ContainsKey("result"))
|
||
{
|
||
dic = HttpExtend.JsonToDictionary(result["result"].ToString());
|
||
}
|
||
if (dic.ContainsKey("code"))
|
||
{
|
||
var code = dic["code"].ToString();
|
||
if (code != "200" && code != "1002016")
|
||
{
|
||
if (dic["message"].ToString().ToLower() == "授权key无权限或错误")
|
||
{
|
||
if (num < 3)
|
||
{
|
||
num++;
|
||
goto Next;
|
||
}
|
||
//if (code == "1002006") throw new Exception("京东key失效");
|
||
throw new Exception(@"授权key无权限或错误");
|
||
}
|
||
else
|
||
{
|
||
EventClient.OnEvent(this, $"京东接口异常:{this.Member.username}({this.Member.usernick})->{api}->{dic["message"]}");
|
||
throw new Exception(HttpHelper.ObjectToJson(dic["message"]));
|
||
}
|
||
}
|
||
}
|
||
return dic;
|
||
}
|
||
else if (dic.ContainsKey("errorResponse") || dic.ContainsKey("error_response"))//{"errorResponse":{"code":"3067","msg":"API GLOBAL LIMITER ACTIVE, REQUEST TIMES LIMITED"}}
|
||
{
|
||
if (dic.ContainsKey("errorResponse"))
|
||
{
|
||
var errorResponse = dic["errorResponse"] as Dictionary<string, object>;
|
||
if (errorResponse.ContainsKey("code") && (errorResponse["code"].ToString() == "3066" || errorResponse["code"].ToString() == "67"))
|
||
{
|
||
AppKeysIndex++;
|
||
if (AppKeysIndex < AppKeys.Length)
|
||
{
|
||
goto Next;
|
||
}
|
||
else
|
||
throw new Exception($".京东调用量饱和,暂时无法为您发起请求!");
|
||
}
|
||
else
|
||
{
|
||
throw new Exception($".京东接口异常:({this.Member.usernick})->{api}->{HttpHelper.ObjectToJson(dic["errorResponse"])}");
|
||
}
|
||
}
|
||
else if (dic.ContainsKey("error_response"))
|
||
{
|
||
var errorResponse = dic["error_response"] as Dictionary<string, object>;
|
||
//if (errorResponse.ContainsKey("code") && errorResponse["code"] == "2")
|
||
//{
|
||
if (errorResponse.ContainsKey("zh_desc") && errorResponse["zh_desc"].ToString().Contains("1天内访问接口"))
|
||
{
|
||
AppKeysIndex++;
|
||
if (AppKeysIndex < AppKeys.Length)
|
||
{
|
||
goto Next;
|
||
}
|
||
else
|
||
throw new Exception($".京东调用量饱和,暂时无法为您发起请求!");
|
||
}
|
||
//}
|
||
else
|
||
{
|
||
string code = string.Empty;
|
||
if (errorResponse.ContainsKey("code"))
|
||
code = errorResponse["code"].ToString();
|
||
throw new Exception($".京东接口异常:({this.Member.usernick})->{api}-> {code}.京东调用量饱和,暂时无法为您发起请求!");
|
||
}
|
||
}
|
||
}
|
||
return new Dictionary<string, object>();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw ex;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 2021年1月25日 新接口
|
||
/// <summary>
|
||
/// 京东下载订单
|
||
/// </summary>
|
||
/// <param name="start_time">开始时间</param>
|
||
/// <param name="end_time">结束时间</param>
|
||
/// <param name="page_index">第几页</param>
|
||
/// <param name="page_size">每页数量,上限500</param>
|
||
/// <param name="type">订单类型</param>
|
||
/// <returns></returns>
|
||
internal Dictionary<string, object> DownOrder(DateTime datetime, string key, int page_index, int page_size, int type = 3)
|
||
{
|
||
int i = 0;
|
||
Next:
|
||
try
|
||
{
|
||
var endTime = datetime.ToString("yyyy-MM-dd HH:mm:ss"); //每小时同步订单
|
||
var startTime = datetime.AddHours(-1).ToString("yyyy-MM-dd HH:mm:ss"); //每小时同步订单
|
||
var _rst = this.SendJingdong("jd.union.open.order.row.query", new
|
||
{
|
||
orderReq = new
|
||
{
|
||
key = key,
|
||
pageIndex = page_index,
|
||
pageSize = page_size,
|
||
type = type, //订单时间查询类型(1:下单时间,2:完成时间(购买用户确认收货时间),3:更新时间
|
||
startTime = startTime, //查询时间,建议使用分钟级查询,格式: yyyyMMddHH, yyyyMMddHHmm或yyyyMMddHHmmss,如201811031212的查询范围从12:12:00-12:12:59
|
||
endTime = endTime,
|
||
}
|
||
});
|
||
return _rst;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
if (i <= 3 && ex.Message == "未将对象引用设置到对象的实例。") //调用接口是偶尔会出现为将对象实例化的错误.
|
||
{
|
||
i++;
|
||
Thread.Sleep(100);
|
||
goto Next;
|
||
}
|
||
else
|
||
throw ex;
|
||
|
||
EventClient.OnEvent(this, $"更新京东订单异常: {ex.Message} - {ex.StackTrace}");
|
||
return null;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#endregion
|
||
|
||
#region Sign计算
|
||
private string SignTopRequest(Dictionary<string, string> parameters, string appSecret)
|
||
{
|
||
StringBuilder sb = new StringBuilder(appSecret);
|
||
//按照规则拼成字符串
|
||
foreach (KeyValuePair<string, string> entry in parameters)
|
||
{
|
||
string name = entry.Key;
|
||
string value = entry.Value;
|
||
//检测参数是否为空
|
||
if (areNotEmpty(new string[] { name, value }))
|
||
{
|
||
sb.Append(name).Append(value);
|
||
}
|
||
}
|
||
sb.Append(appSecret);
|
||
|
||
//MD5
|
||
return md5(sb.ToString());
|
||
}
|
||
|
||
private string md5(string source)
|
||
{
|
||
MD5 md5 = MD5.Create();
|
||
var result = md5.ComputeHash(Encoding.UTF8.GetBytes(source));
|
||
return byte2hex(result);
|
||
}
|
||
|
||
private string byte2hex(byte[] bytes)
|
||
{
|
||
//把二进制转化为大写的十六进制
|
||
StringBuilder result = new StringBuilder();
|
||
for (int i = 0; i < bytes.Length; i++)
|
||
{
|
||
result.Append(bytes[i].ToString("X2"));
|
||
}
|
||
return result.ToString();
|
||
}
|
||
|
||
private bool areNotEmpty(string[] values)
|
||
{
|
||
bool result = true;
|
||
if ((values == null) || (values.Length == 0))
|
||
result = false;
|
||
else
|
||
{
|
||
foreach (var value in values)
|
||
{
|
||
result &= !isEmpty(value);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
private bool isEmpty(string value)
|
||
{
|
||
int strLen;
|
||
if ((value == null) || ((strLen = value.Length) == 0))
|
||
return true;
|
||
for (int i = 0; i < strLen; i++)
|
||
{
|
||
var ss = value[i];
|
||
if (!string.IsNullOrWhiteSpace(value[i].ToString()))
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
#endregion
|
||
}
|
||
}
|