You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
12 KiB
C#

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
{
/// <summary>
/// 快递
/// </summary>
public class SFDeliverySend : IDeliverySend, ITransient
{
/// <summary>
/// Redis中用于保存访问顺丰API的Token的键名
/// </summary>
private const string SF_API_TOKEN_KEY = $"SfApiToken";
private readonly ISysCacheService cache;
private readonly ILogger<ExpressDeliveryService> logger;
public SFDeliverySend(ISysCacheService cache, ILogger<ExpressDeliveryService> logger)
{
this.cache = cache;
this.logger = logger;
}
#region IDeliverySend接口实现
public async Task<string> CreateOrder(ExpressDeliveryOrder order)
{
SFCreateOrderDto sFSend = new SFCreateOrderDto()
{
orderId = order.Id.ToString(),
language = "zh_CN",
cargoDetails = new List<CargoDetailsItem>()
{
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<ContactInfoListItem> 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<JObject>();
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<bool> 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<string, object>() { { "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
/// <summary>
/// 请求获取顺丰面单Pdf下载地址及访问Token
/// </summary>
/// <returns></returns>
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<SFPrintWaybillDto.Document>() {
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<JObject>();
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);
}
}
/// <summary>
/// 构建请求顺丰接口所需的Url及Body参数
/// </summary>
/// <param name="apiCode">接口编码</param>
/// <param name="businessData">参数</param>
/// <returns></returns>
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<string, string>()
{
{ "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<string, object>()
{
{ "partnerID", partnerId },
{ "serviceCode", apiCode},
{ "requestID", Guid.NewGuid().ToString()},
{ "timestamp", DateTimeOffset.Now.ToUnixTimeSeconds()},
{ "accessToken", accessToken},
{ "msgData", businessData}
};
return (url + "/std/service", dict);
}
}
}