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#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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);
}
}
}