using Furion.DependencyInjection;
using Furion.FriendlyException;
using Furion.RemoteRequest.Extensions;
using Microsoft.Extensions.Logging;
using Myshipping.Application.Entity;
using Myshipping.Application.Service.ExpressDelivery.Dto;
using Myshipping.Core;
using Myshipping.Core.Service;
using Newtonsoft.Json.Linq;
using StackExchange.Profiling.Internal;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Myshipping.Application
{
///
/// 快递
///
public class SFDeliverySend : IDeliverySend, ITransient
{
///
/// Redis中用于保存访问顺丰API的Token的键名
///
private const string SF_API_TOKEN_KEY = $"SfApiToken";
private readonly ISysCacheService cache;
private readonly ILogger logger;
public SFDeliverySend(ISysCacheService cache, ILogger logger)
{
this.cache = cache;
this.logger = logger;
}
#region IDeliverySend接口实现
public async Task CreateOrder(ExpressDeliveryOrder order)
{
SFCreateOrderDto sFSend = new SFCreateOrderDto()
{
orderId = order.Id.ToString(),
language = "zh_CN",
cargoDetails = new List()
{
new CargoDetailsItem()
{
count = (int)order.KDNum,
name = order.GOODSNAME
}
}
};
if (order.SettleAccountsTypeCode == "2")
{
if (string.IsNullOrWhiteSpace(order.MonthlyCard))
{
throw Oops.Bah("当结费类型为月结时,月结卡号不能为空");
}
sFSend.monthlyCard = order.MonthlyCard;
}
List contactList = new()
{
new ContactInfoListItem
{
address = $"{order.FJProvince}{order.FJCity}{order.FJAddress}",
contact = order.FJPeople,
contactType = 1,
country = "CN",
tel = order.FJTel,
},
new ContactInfoListItem
{
address = $"{order.SJProvince}{order.SJCity}{order.SJAddress}",
contact = order.SJPeople,
contactType = 2,
country = "CN",
tel = order.SJTel,
}
};
sFSend.contactInfoList = contactList;
(string url, object body) apiParam = await BuildApiParamAsync("EXP_RECE_CREATE_ORDER", sFSend);
logger.LogInformation("调用顺丰下单接口,参数:" + apiParam.body.ToJson());
var strRtn = await apiParam.url.SetBody(apiParam.body, "application/x-www-form-urlencoded").PostAsStringAsync();
logger.LogInformation("调用顺丰下单接口,返回:" + strRtn);
var jobj = strRtn.ToJObject();
if (jobj.GetStringValue("apiResultCode") == "A1000")
{
var resultDataObj = jobj.GetStringValue("apiResultData").ToJObject();
bool isSuccess = resultDataObj.GetBooleanValue("success");
if (isSuccess)
{
var waybillNoInfoObj = resultDataObj.GetJObjectValue("msgData").GetJArrayValue("waybillNoInfoList")[0].Value();
var waybillNo = waybillNoInfoObj.GetStringValue("waybillNo");
return waybillNo;
}
else
{
var errorMsg = resultDataObj.GetStringValue("errorMsg");
logger.LogInformation("调用顺丰下单接口,返回异常,errorMsg:" + errorMsg);
throw Oops.Bah(errorMsg);
}
}
else
{
var apiErrorMsg = jobj.GetStringValue("apiErrorMsg");
logger.LogInformation("调用顺丰下单接口,返回异常,apiErrorMsg:" + apiErrorMsg);
throw Oops.Bah(apiErrorMsg);
}
}
public async Task CancelOrder(string orderId)
{
var param = new
{
dealType = 2,
orderId
};
(string url, object body) apiParam = await BuildApiParamAsync("EXP_RECE_UPDATE_ORDER", param);
logger.LogInformation($"调用顺丰订单取消接口,参数:{apiParam.body.ToJson()}");
var strRtn = await apiParam.url.SetBody(apiParam.body, "application/x-www-form-urlencoded").PostAsStringAsync();
logger.LogInformation($"调用顺丰订单取消接口,返回:{strRtn}");
var jobj = strRtn.ToJObject();
if (jobj.GetStringValue("apiResultCode") == "A1000")
{
var resultDataObj = jobj.GetStringValue("apiResultData").ToJObject();
if (resultDataObj.GetBooleanValue("success"))
{
return true;
}
else
{
var errorMsg = resultDataObj.GetStringValue("errorMsg");
logger.LogInformation($"顺丰取消快递下单接口,返回异常,errorMsg:{errorMsg}");
throw Oops.Bah(errorMsg);
}
}
else
{
var apiErrorMsg = jobj.GetStringValue("apiErrorMsg");
logger.LogInformation($"顺丰取消快递下单接口,返回异常:{apiErrorMsg}");
throw Oops.Bah(apiErrorMsg);
}
}
public async Task DownloadWaybillFile(string waybillNo, string savePath)
{
logger.LogInformation($"调用顺丰面单PDF下载接口开始...");
var waybillDownloadInfo = await GetWaybillDownloadInfo(waybillNo);
var header = new Dictionary() { { "X-Auth-token", waybillDownloadInfo.Token } };
using var stream = await waybillDownloadInfo.Url.SetHeaders(header).GetAsStreamAsync();
using FileStream fs = new FileStream(savePath, FileMode.Create);
await stream.CopyToAsync(fs);
logger.LogInformation($"顺丰面单PDF文件保存完成,保存地址:{savePath}");
}
#endregion
///
/// 请求获取顺丰面单Pdf下载地址及访问Token
///
///
private async Task<(string Url, string Token)> GetWaybillDownloadInfo(string masterWaybillNo)
{
SFPrintWaybillDto sFSend = new SFPrintWaybillDto()
{
fileType = "pdf",
sync = true,
templateCode = "fm_150_standard_DSWYR7LUN7FB",
version = "2.0",
documents = new List() {
new SFPrintWaybillDto.Document(masterWaybillNo)
}
};
(string url, object body) apiParam = await BuildApiParamAsync("COM_RECE_CLOUD_PRINT_WAYBILLS", sFSend);
logger.LogInformation("调用顺丰获取顺丰面单Pdf下载地址接口,参数:" + apiParam.body.ToJson());
var strRtn = await apiParam.url.SetBody(apiParam.body, "application/x-www-form-urlencoded").PostAsStringAsync();
logger.LogInformation("调用顺丰获取顺丰面单Pdf下载地址接口,返回:" + strRtn);
var jobj = strRtn.ToJObject();
if (jobj.GetStringValue("apiResultCode") == "A1000")
{
var resultDataObj = jobj.GetStringValue("apiResultData").ToJObject();
bool isSuccess = resultDataObj.GetBooleanValue("success");
if (isSuccess)
{
var fileObj = resultDataObj.GetJObjectValue("obj").GetJArrayValue("files")[0].Value();
var token = fileObj.GetStringValue("token");
var url = fileObj.GetStringValue("url");
return (url, token);
}
else
{
var errorMsg = resultDataObj.GetStringValue("errorMsg");
logger.LogInformation("调用顺丰获取顺丰面单Pdf下载地址接口,返回异常,errorMsg:" + errorMsg);
throw Oops.Bah(errorMsg);
}
}
else
{
var apiErrorMsg = jobj.GetStringValue("apiErrorMsg");
logger.LogInformation("调用顺丰获取顺丰面单Pdf下载地址接口,返回异常,apiErrorMsg:" + apiErrorMsg);
throw Oops.Bah(apiErrorMsg);
}
}
///
/// 构建请求顺丰接口所需的Url及Body参数
///
/// 接口编码
/// 参数
///
private async Task<(string url, object body)> BuildApiParamAsync(string apiCode, object businessData)
{
var cacheDictData = await cache.GetAllDictData();
var url = cacheDictData.FirstOrDefault(x => x.TypeCode == "url_set" && x.Code == "sf_api_url")?.Value;
if (string.IsNullOrEmpty(url))
{
throw Oops.Bah("需配置顺丰Api接口字典参数:url地址(sf_api_url)");
}
var partnerId = cacheDictData.FirstOrDefault(x => x.TypeCode == "SFCode" && x.Code == "partnerId")?.Value;
if (string.IsNullOrEmpty(partnerId))
{
throw Oops.Bah("需配置顺丰Api接口字典参数:合作伙伴编码,即顾客编码(partnerId)");
}
string accessToken;
if (cache.Exists(SF_API_TOKEN_KEY))
{
accessToken = cache.Get(SF_API_TOKEN_KEY);
}
else
{
var secret = cacheDictData.FirstOrDefault(x => x.TypeCode == "SFCode" && x.Code == "secret")?.Value;
if (string.IsNullOrEmpty(partnerId))
{
throw Oops.Bah("需配置顺丰Api接口字典参数:合作伙伴密钥,即校验码(secret)");
}
var getTokenParam = new Dictionary()
{
{ "partnerID",partnerId },
{ "grantType", "password"},
{ "secret", secret}
};
logger.LogInformation("调用顺丰Token查询接口,参数:" + getTokenParam.ToJson());
var strRtn = await (url + "/oauth2/accessToken").SetBody(getTokenParam, "application/x-www-form-urlencoded").PostAsStringAsync();
logger.LogInformation("调用顺丰Token查询接口,返回:" + strRtn);
var jobj = strRtn.ToJObject();
if (jobj.GetStringValue("apiResultCode") == "A1000")
{
accessToken = jobj.GetStringValue("accessToken");
//设置token过期时间2小时
await cache.SetTimeoutAsync(SF_API_TOKEN_KEY, accessToken, TimeSpan.FromHours(2));
}
else
{
throw Oops.Bah("调用顺丰Token查询接口时发生异常:" + jobj.GetStringValue("apiErrorMsg"));
}
}
var dict = new Dictionary()
{
{ "partnerID", partnerId },
{ "serviceCode", apiCode},
{ "requestID", Guid.NewGuid().ToString()},
{ "timestamp", DateTimeOffset.Now.ToUnixTimeSeconds()},
{ "accessToken", accessToken},
{ "msgData", businessData}
};
return (url + "/std/service", dict);
}
}
}