|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Diagnostics;
|
|
|
using System.Linq;
|
|
|
using System.Net;
|
|
|
using System.Net.Http;
|
|
|
using System.Net.Http.Headers;
|
|
|
using System.Security.Cryptography;
|
|
|
using System.Text;
|
|
|
using System.Threading;
|
|
|
using POSV.Utils;
|
|
|
|
|
|
namespace POSV.Dianping
|
|
|
{
|
|
|
public class MeituanCouponUtils
|
|
|
{
|
|
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
|
|
|
|
|
private static object _lock = new object();
|
|
|
|
|
|
private static HttpClient httpClient = null;
|
|
|
|
|
|
private static MeituanCouponUtils _instance = null;
|
|
|
|
|
|
private static string baseUrl = "https://open-api.dianping.com";
|
|
|
|
|
|
private static string version = "2.0";
|
|
|
|
|
|
public static string TEST_APPKEY = "7e75fa08b18f5fcd867e1d1cd58e38ea72fb33c1";
|
|
|
|
|
|
public static string TEST_APPSECRET = "34d4da25d45b4f812ab99a7ab608b50e7d3bbf10";
|
|
|
|
|
|
public static MeituanCouponUtils Instance
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
if (_instance == null)
|
|
|
{
|
|
|
lock (_lock)
|
|
|
{
|
|
|
|
|
|
//remove limits from service point manager
|
|
|
ServicePointManager.MaxServicePoints = 10000;
|
|
|
ServicePointManager.DefaultConnectionLimit = 10000;
|
|
|
ServicePointManager.CheckCertificateRevocationList = true;
|
|
|
ServicePointManager.Expect100Continue = false;
|
|
|
ServicePointManager.MaxServicePointIdleTime = 1000 * 30;
|
|
|
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
|
|
|
ServicePointManager.UseNagleAlgorithm = false;
|
|
|
ServicePointManager.DnsRefreshTimeout = 0;
|
|
|
//提升系统外联的最大并发web访问数
|
|
|
ServicePointManager.DefaultConnectionLimit = 1024;
|
|
|
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
|
|
|
|
|
|
|
|
|
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3
|
|
|
// | SecurityProtocolType.Tls
|
|
|
// | (SecurityProtocolType)0x300 //Tls11
|
|
|
// | (SecurityProtocolType)0xC00; //Tls12
|
|
|
|
|
|
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
|
|
|
// | (SecurityProtocolType)0x300 //Tls11
|
|
|
// | (SecurityProtocolType)0xC00; //Tls12
|
|
|
|
|
|
var handler = new HttpClientHandler();
|
|
|
if (handler.SupportsAutomaticDecompression)
|
|
|
{
|
|
|
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
|
|
|
}
|
|
|
|
|
|
handler.Proxy = null;
|
|
|
handler.UseProxy = false;
|
|
|
handler.UseDefaultCredentials = false;
|
|
|
|
|
|
httpClient = new HttpClient(handler);
|
|
|
|
|
|
Interlocked.CompareExchange(ref httpClient, new HttpClient(handler) { BaseAddress = new Uri(baseUrl) }, null);
|
|
|
|
|
|
httpClient.DefaultRequestHeaders.Connection.Add("Keep-Alive");
|
|
|
httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36");
|
|
|
httpClient.DefaultRequestHeaders.Add("Accept", "*/*");
|
|
|
httpClient.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate"); //声明给服务器,客户端支持gzip解压
|
|
|
httpClient.DefaultRequestHeaders.Add("Accept-Language", "zh-cn,en-us;q=0.7,en;q=0.3");
|
|
|
httpClient.DefaultRequestHeaders.Add("Pragma", "no-cache");
|
|
|
httpClient.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
|
|
|
|
|
|
httpClient.DefaultRequestHeaders.Add("ES-API-VERSION", "1.0.0");
|
|
|
|
|
|
|
|
|
httpClient.DefaultRequestHeaders.ExpectContinue = false;
|
|
|
|
|
|
httpClient.Timeout = new TimeSpan(0, 0, 20);
|
|
|
|
|
|
_instance = new MeituanCouponUtils();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return _instance;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
public string PostAsync(string requestUrl, SortedList<string, object> parameters)
|
|
|
{
|
|
|
Stopwatch watch = Stopwatch.StartNew();
|
|
|
try
|
|
|
{
|
|
|
string appKey = Global.StoreInfo.StoreInfoMessage.MeituanAppKey;
|
|
|
string appSecret = Global.StoreInfo.StoreInfoMessage.MeituanAppSecret;
|
|
|
|
|
|
if (!string.IsNullOrEmpty(appKey))
|
|
|
{
|
|
|
appKey = appKey.Trim();
|
|
|
}
|
|
|
|
|
|
if (!string.IsNullOrEmpty(appSecret))
|
|
|
{
|
|
|
appSecret = appSecret.Trim();
|
|
|
}
|
|
|
|
|
|
var stringParams = JsonUtils.Serialize(parameters);
|
|
|
var content = new StringContent(stringParams, Encoding.UTF8, "application/json");
|
|
|
|
|
|
string timestamp = this.GetTimestamp();
|
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
sb.Append(baseUrl).Append(requestUrl);
|
|
|
sb.Append("?");
|
|
|
sb.Append("appKey").Append("=").Append(appKey).Append("&");
|
|
|
sb.Append("ts").Append("=").Append(timestamp).Append("&");
|
|
|
sb.Append("version").Append("=").Append(version).Append("&");
|
|
|
var sign = this.Sign(appKey, appSecret, timestamp, version, parameters);
|
|
|
sb.Append("sign").Append("=").Append(sign);
|
|
|
|
|
|
string url = sb.ToString();
|
|
|
var task = httpClient.PostAsync(url, content);
|
|
|
task.Result.EnsureSuccessStatusCode();
|
|
|
|
|
|
HttpResponseMessage response = task.Result;
|
|
|
|
|
|
var result = response.Content.ReadAsStringAsync();
|
|
|
return result.Result;
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
string message = "调用PostAsync方法发生错误,XP系统不支持";
|
|
|
logger.Error(ex, message);
|
|
|
|
|
|
throw new Exception(message);
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
logger.Info("耗时<{0}>毫秒", watch.ElapsedMilliseconds);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public Tuple<bool, string, TuangouOrderConsumeResponse> OrderConsume(TuangouOrderConsumeRequest request)
|
|
|
{
|
|
|
Tuple<bool, string, TuangouOrderConsumeResponse> result = null;
|
|
|
try
|
|
|
{
|
|
|
var parameters = new SortedList<string, object>();
|
|
|
parameters.Add("vendorOrderId", request.VendorOrderId);
|
|
|
parameters.Add("totalAmount", request.TotalAmount);
|
|
|
parameters.Add("orderSkus", request.OrderSkus);
|
|
|
parameters.Add("couponCode", request.CouponCode);
|
|
|
parameters.Add("vendorShopId", request.VendorShopId);
|
|
|
parameters.Add("eId", request.EId);
|
|
|
parameters.Add("eName", request.EName);
|
|
|
|
|
|
|
|
|
string response = Instance.PostAsync("/tuangou/coupon/orderConsume", parameters);
|
|
|
|
|
|
var data = JsonUtils.Deserialize<BasicResponseModel<TuangouOrderConsumeResponse>>(response);
|
|
|
|
|
|
if (data.Code == 200)
|
|
|
{
|
|
|
result = new Tuple<bool, string, TuangouOrderConsumeResponse>(true, data.Message, data.Content);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
|
|
|
result = new Tuple<bool, string, TuangouOrderConsumeResponse>(false, string.Format("{0}<{1}>", data.Message, data.Code), null);
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
logger.Error(ex, "券码核销异常");
|
|
|
|
|
|
result = new Tuple<bool, string, TuangouOrderConsumeResponse>(false, "券码核销异常,本次操作无效", null);
|
|
|
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
public Tuple<bool, string, CouponCancelResponse> OrdeCancel(CouponCancelRequest request)
|
|
|
{
|
|
|
Tuple<bool, string, CouponCancelResponse> result = null;
|
|
|
try
|
|
|
{
|
|
|
var parameters = new SortedList<string, object>();
|
|
|
parameters.Add("couponCode", request.CouponCode);
|
|
|
parameters.Add("vendorShopId", request.VendorShopId);
|
|
|
parameters.Add("eId", request.EId);
|
|
|
parameters.Add("eName", request.EName);
|
|
|
|
|
|
|
|
|
string response = Instance.PostAsync("/tuangou/coupon/cancel", parameters);
|
|
|
|
|
|
var data = JsonUtils.Deserialize<BasicResponseModel<CouponCancelResponse>>(response);
|
|
|
|
|
|
if (data.Code == 200)
|
|
|
{
|
|
|
result = new Tuple<bool, string, CouponCancelResponse>(true, data.Message, data.Content);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
|
|
|
result = new Tuple<bool, string, CouponCancelResponse>(false, string.Format("{0}<{1}>", data.Message, data.Code), null);
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
logger.Error(ex, "撤销验券异常");
|
|
|
|
|
|
result = new Tuple<bool, string, CouponCancelResponse>(false, "撤销验券异常,本次操作无效", null);
|
|
|
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 券状态查询,通过该接口可获取到团购券的基本信息。
|
|
|
/// </summary>
|
|
|
/// <param name="appKey"></param>
|
|
|
/// <param name="appSecret"></param>
|
|
|
/// <param name="request"></param>
|
|
|
/// <returns></returns>
|
|
|
public Tuple<bool, string, CouponStatusQueryResponse> CouponQuery(CouponStatusQueryRequest request)
|
|
|
{
|
|
|
Tuple<bool, string, CouponStatusQueryResponse> result = null;
|
|
|
try
|
|
|
{
|
|
|
var parameters = new SortedList<string, object>();
|
|
|
parameters.Add("couponCode", request.CouponCode);
|
|
|
parameters.Add("vendorShopId", request.VendorShopId);
|
|
|
|
|
|
string response = Instance.PostAsync("/tuangou/coupon/status/query", parameters);
|
|
|
|
|
|
var data = JsonUtils.Deserialize<BasicResponseModel<CouponStatusQueryResponse>>(response);
|
|
|
|
|
|
if(data.Code == 200)
|
|
|
{
|
|
|
result = new Tuple<bool, string, CouponStatusQueryResponse>(true, data.Message, data.Content);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
|
|
|
result = new Tuple<bool, string, CouponStatusQueryResponse>(false, string.Format("{0}<{1}>",data.Message,data.Code), null);
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
logger.Error(ex, "查询券状态异常");
|
|
|
|
|
|
result = new Tuple<bool, string, CouponStatusQueryResponse>(false, "查询券状态异常,本次操作无效", null);
|
|
|
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
public Tuple<bool, string, CouponLocalRecordResponse> QueryLocalListByDate(CouponLocalRecordRequest request)
|
|
|
{
|
|
|
Tuple<bool, string, CouponLocalRecordResponse> result = null;
|
|
|
try
|
|
|
{
|
|
|
var parameters = new SortedList<string, object>();
|
|
|
parameters.Add("vendorShopId", request.VendorShopId);
|
|
|
parameters.Add("date", request.Date);
|
|
|
parameters.Add("offset", request.Offset);
|
|
|
parameters.Add("limit", request.Limit);
|
|
|
if(request.VendorOrderId != null && request.VendorOrderId.Count() > 0)
|
|
|
{
|
|
|
parameters.Add("vendorOrderId", request.VendorOrderId);
|
|
|
}
|
|
|
|
|
|
|
|
|
string response = Instance.PostAsync("/tuangou/coupon/queryLocalListByDate", parameters);
|
|
|
|
|
|
var data = JsonUtils.Deserialize<BasicResponseModel<CouponLocalRecordResponse>>(response);
|
|
|
|
|
|
if (data.Code == 200)
|
|
|
{
|
|
|
result = new Tuple<bool, string, CouponLocalRecordResponse>(true, data.Message, data.Content);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
|
|
|
result = new Tuple<bool, string, CouponLocalRecordResponse>(false, string.Format("{0}<{1}>", data.Message, data.Code), null);
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
logger.Error(ex, "查询门店验券历史异常");
|
|
|
|
|
|
result = new Tuple<bool, string, CouponLocalRecordResponse>(false, "查询门店验券历史异常,本次操作无效", null);
|
|
|
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取当前时间戳,兼容Java服务端
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
public string GetTimestamp()
|
|
|
{
|
|
|
TimeSpan ts = new TimeSpan(DateTime.UtcNow.Ticks - new DateTime(1970, 1, 1, 0, 0, 0).Ticks);
|
|
|
|
|
|
return Convert.ToString((long)ts.TotalMilliseconds);
|
|
|
}
|
|
|
|
|
|
|
|
|
public string Sign(string appKey, string appSecret,string ts,string version, SortedList<string, object> param)
|
|
|
{
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
sb.Append(appSecret);
|
|
|
sb.Append("appKey").Append(appKey);
|
|
|
sb.Append("ts").Append(ts);
|
|
|
sb.Append("version").Append(version);
|
|
|
|
|
|
if(param == null)
|
|
|
{
|
|
|
param = new SortedList<string, object>();
|
|
|
}
|
|
|
|
|
|
sb.Append(JsonUtils.Serialize(param));
|
|
|
|
|
|
logger.Info("签名字符串:{0}", sb.ToString());
|
|
|
|
|
|
SHA1 sha1 = new SHA1CryptoServiceProvider();//建立一个SHA1
|
|
|
byte[] source = Encoding.UTF8.GetBytes(sb.ToString());//将字符串转为Byte[]
|
|
|
byte[] crypto = sha1.ComputeHash(source);//进行SHA1加密
|
|
|
return ByteArrayToHexString(crypto);//把加密后的字符串从Byte[]转为字符串
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 字节数组转十六进制字符串
|
|
|
/// </summary>
|
|
|
/// <param name="data"></param>
|
|
|
/// <returns></returns>
|
|
|
public string ByteArrayToHexString(byte[] data)
|
|
|
{
|
|
|
StringBuilder sb = new StringBuilder(data.Length * 3);
|
|
|
foreach (byte b in data)
|
|
|
{
|
|
|
sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
|
|
|
}
|
|
|
return sb.ToString().ToLower();
|
|
|
|
|
|
}
|
|
|
}
|
|
|
}
|