| | |
| | | using Aop.Api.Domain; |
| | | using ApiTools.Core.Utils.NongYePayUtils.Models; |
| | | using Azure; |
| | | using Azure.Core; |
| | | using Furion; |
| | | using Furion.DatabaseAccessor; |
| | | using Furion.DataEncryption.Extensions; |
| | | using Furion.DependencyInjection; |
| | | using Furion.DistributedIDGenerator; |
| | | using Furion.FriendlyException; |
| | | using Furion.HttpRemote; |
| | | using Microsoft.Extensions.Options; |
| | | using NetTopologySuite.Algorithm; |
| | | using Org.BouncyCastle.Asn1.Ocsp; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Diagnostics; |
| | | using System.IO; |
| | | using System.IO.Compression; |
| | | using System.Linq; |
| | | using System.Net.Sockets; |
| | | using System.Text; |
| | |
| | | namespace ApiTools.Core |
| | | { |
| | | public class NongYePayUtils( |
| | | IHttpRemoteService httpRemoteService, |
| | | IOptions<NongYePayOptions> options, |
| | | IRepository<ThreeResourceLog, LogDbContextLocator> repThreeResourceLog |
| | | ) : ITransient |
| | | { |
| | | private readonly IHttpRemoteService httpRemoteService = httpRemoteService; |
| | | private readonly IOptions<NongYePayOptions> options = options; |
| | | private readonly IRepository<ThreeResourceLog, LogDbContextLocator> repThreeResourceLog = repThreeResourceLog; |
| | | |
| | |
| | | return Send<NongYePayGetBalanceRequest, NongYePayGetBalanceResponse>(request); |
| | | } |
| | | |
| | | private async Task<TResponse> Send<TRequest, TResponse>(TRequest request) |
| | | where TRequest : NongYePayBaseRequest |
| | | where TResponse : NongYePayBaseResponse |
| | | /// <summary> |
| | | /// 单笔对公转账 |
| | | /// </summary> |
| | | /// <param name="request"></param> |
| | | /// <returns></returns> |
| | | public Task<NongYePaySingleCorporateTransferResponse> SingleCorporateTransfer(NongYePaySingleCorporateTransferRequest request) |
| | | { |
| | | var logier = JwtUtils.GetCurrentLogier(); |
| | | return Send<NongYePaySingleCorporateTransferRequest, NongYePaySingleCorporateTransferResponse>(request); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 查询转账状态 |
| | | /// </summary> |
| | | /// <param name="request"></param> |
| | | /// <returns></returns> |
| | | public Task<NongYePayGetTransferStatusResponse> GetTransferStatus(NongYePayGetTransferStatusRequest request) |
| | | { |
| | | return Send<NongYePayGetTransferStatusRequest, NongYePayGetTransferStatusResponse>(request); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 查询转账详情 |
| | | /// </summary> |
| | | /// <param name="request"></param> |
| | | /// <returns></returns> |
| | | public Task<NongYePayGetTransferDetailsResponse> GetTransferDetails(NongYePayGetTransferDetailsRequest request) |
| | | { |
| | | return SendWithFile<NongYePayGetTransferDetailsRequest, NongYePayGetTransferDetailsResponse, NongYePayGetTransferDetailsResponseItem>(request); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取电子回单 |
| | | /// </summary> |
| | | /// <param name="request"></param> |
| | | /// <returns></returns> |
| | | public Task<NongYePayGetEreceiptsResponse> GetEreceipts(NongYePayGetEreceiptsRequest request) |
| | | { |
| | | return SendWithFile<NongYePayGetEreceiptsRequest, NongYePayGetEreceiptsResponse, NongYePayGetEreceiptsResponseItem>(request); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 下载电子回单 |
| | | /// </summary> |
| | | /// <param name="request"></param> |
| | | /// <returns></returns> |
| | | public async Task<NongYePayDownloadEreceiptResponse> DownloadEreceipt(NongYePayDownloadEreceiptRequest request) |
| | | { |
| | | return await SendWithZip<NongYePayDownloadEreceiptRequest, NongYePayDownloadEreceiptResponse>(request); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 实时下载电子回单 |
| | | /// </summary> |
| | | /// <param name="request"></param> |
| | | /// <returns></returns> |
| | | public async Task<NongYePayRealTimeDownloadEreceiptResponse> RealTimeDownloadEreceipt(NongYePayRealTimeDownloadEreceiptRequest request) |
| | | { |
| | | return await SendWithZip<NongYePayRealTimeDownloadEreceiptRequest, NongYePayRealTimeDownloadEreceiptResponse>(request); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取银行地区 |
| | | /// </summary> |
| | | /// <returns></returns> |
| | | public Task<List<SelectOption<string, string>>> GetBankAreas() |
| | | { |
| | | var list = new List<SelectOption<string, string>> |
| | | { |
| | | new SelectOption<string, string>("02", "天津市"), |
| | | new SelectOption<string, string>("03", "上海"), |
| | | new SelectOption<string, string>("04", "山西省"), |
| | | new SelectOption<string, string>("05", "内蒙古"), |
| | | new SelectOption<string, string>("06", "辽宁省"), |
| | | new SelectOption<string, string>("07", "吉林省"), |
| | | new SelectOption<string, string>("08", "黑龙江"), |
| | | new SelectOption<string, string>("10", "江苏省"), |
| | | new SelectOption<string, string>("11", "北京市"), |
| | | new SelectOption<string, string>("12", "安徽省"), |
| | | new SelectOption<string, string>("13", "福建省"), |
| | | new SelectOption<string, string>("14", "江西省"), |
| | | new SelectOption<string, string>("15", "山东省"), |
| | | new SelectOption<string, string>("16", "河南省"), |
| | | new SelectOption<string, string>("17", "湖北省"), |
| | | new SelectOption<string, string>("18", "湖南省"), |
| | | new SelectOption<string, string>("19", "浙江省"), |
| | | new SelectOption<string, string>("20", "广西区"), |
| | | new SelectOption<string, string>("21", "海南省"), |
| | | new SelectOption<string, string>("22", "四川省"), |
| | | new SelectOption<string, string>("23", "贵州省"), |
| | | new SelectOption<string, string>("24", "云南省"), |
| | | new SelectOption<string, string>("25", "西藏区"), |
| | | new SelectOption<string, string>("26", "陕西省"), |
| | | new SelectOption<string, string>("27", "甘肃省"), |
| | | new SelectOption<string, string>("28", "青海省"), |
| | | new SelectOption<string, string>("29", "宁夏区"), |
| | | new SelectOption<string, string>("30", "新疆区"), |
| | | new SelectOption<string, string>("31", "重庆市"), |
| | | new SelectOption<string, string>("34", "大连市"), |
| | | new SelectOption<string, string>("38", "青岛市"), |
| | | new SelectOption<string, string>("39", "宁波市"), |
| | | new SelectOption<string, string>("40", "厦门市"), |
| | | new SelectOption<string, string>("41", "深圳市"), |
| | | new SelectOption<string, string>("44", "广东省"), |
| | | new SelectOption<string, string>("50", "河北省"), |
| | | new SelectOption<string, string>("71", "台湾省"), |
| | | new SelectOption<string, string>("81", "营业部"), |
| | | new SelectOption<string, string>("97", "香港"), |
| | | new SelectOption<string, string>("98", "澳门"), |
| | | new SelectOption<string, string>("99", "总行"), |
| | | }; |
| | | return Task.FromResult(list); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取银行 |
| | | /// </summary> |
| | | /// <param name="request"></param> |
| | | /// <returns></returns> |
| | | public Task<NongYePayGetBanksResponse> GetBanks(NongYePayGetBanksRequest request) |
| | | { |
| | | return SendWithFile<NongYePayGetBanksRequest, NongYePayGetBanksResponse, NongYePayGetBanksResponseItem>(request); |
| | | } |
| | | |
| | | public string GetSeqNo() |
| | | { |
| | | var now = DateTime.Now; |
| | | var random = StringUtils.GenerateRandomString(6); |
| | | request.CorpNo = options.Value.CorpNo; |
| | | request.ReqDate = now.ToString("yyyyMMdd"); |
| | | request.ReqTime = now.ToString("HHmmss"); |
| | | request.ReqSeqNo = $"{now:yyyyMMddHHmmssfff}{random}"; |
| | | request.ChannelType = "ERP"; |
| | | request.ProductID = "ICC"; |
| | | return $"{now:yyyyMMddHHmmssfff}{random}"; |
| | | } |
| | | |
| | | string requestXml = SerializeRequest(request); |
| | | var log = new ThreeResourceLog |
| | | private async Task<List<NongYePayResponseZipFile>> GetZipFiles(NongYePayZipResponse response) |
| | | { |
| | | var list = new List<NongYePayResponseZipFile>(); |
| | | if (response != null |
| | | && response.RespSource == "0" |
| | | && response.Cmp != null |
| | | && response.Cmp.BatchFileName.IsNotNull()) |
| | | { |
| | | CreatedTime = DateTimeOffset.Now, |
| | | Id = IDGen.NextID(), |
| | | TraceId = App.GetTraceId(), |
| | | Method = EnumResourceMethod.Post, |
| | | Domain = $"{options.Value.Ip}:{options.Value.Port}", |
| | | Path = request.TransCode, |
| | | CreatedUserId = logier?.Id, |
| | | CreatedChannelId = logier?.ChannelId, |
| | | Request = request.ToJson(), |
| | | }; |
| | | await repThreeResourceLog.InsertNowAsync(log); |
| | | var stopwatch = Stopwatch.StartNew(); |
| | | var responseXml = await SendRequest(requestXml); |
| | | stopwatch.Stop(); |
| | | var response = DeserializeResponse<TResponse>(responseXml); |
| | | log.UpdatedTime = DateTimeOffset.Now; |
| | | log.Response = responseXml; |
| | | log.IsSuccess = response.RespSource == "0"; |
| | | log.ElapsedMilliseconds = stopwatch.ElapsedMilliseconds; |
| | | await repThreeResourceLog.UpdateNowAsync(log); |
| | | var memoryStream = new MemoryStream(); |
| | | if (options.Value.RemoteFileUrl.IsNotNull()) |
| | | { |
| | | response.ZipFileName = response.Cmp.BatchFileName; |
| | | var url = $"{options.Value.RemoteFileUrl}/api/file/download"; |
| | | var command = new DownloadFileCommand |
| | | { |
| | | Scene = "NongYePay", |
| | | Path = response.Cmp.BatchFileName |
| | | }; |
| | | var timestamp = DateTime.Now.ToTimeStamp(true); |
| | | var sign = $"POST|/api/file/download|{command.ToJson()}|{options.Value.RemoteFilePrivateKey}|{timestamp}".ToMD5Encrypt(); |
| | | using var stream = await httpRemoteService.PostAsStreamAsync(url, |
| | | builder => builder |
| | | .SetJsonContent(command) |
| | | .WithHeader("x-timestamp", timestamp, replace: true) |
| | | .WithHeader("x-sign", sign, replace: true)); |
| | | stream.CopyTo(memoryStream); |
| | | memoryStream.Position = 0; |
| | | } |
| | | else if (options.Value.FilePath.IsNotNull()) |
| | | { |
| | | response.ZipFileName = $"{options.Value.FilePath}{response.Cmp.BatchFileName}"; |
| | | if (File.Exists(response.ZipFileName)) |
| | | { |
| | | using var stream = File.OpenRead(response.ZipFileName); |
| | | stream.CopyTo(memoryStream); |
| | | memoryStream.Position = 0; |
| | | } |
| | | } |
| | | |
| | | if (!log.IsSuccess) throw Oops.Oh(EnumErrorCodeType.s510, $"农行接口异常:{response.RespInfo}"); |
| | | using (var archive = new ZipArchive(memoryStream)) |
| | | { |
| | | foreach (var entry in archive.Entries) |
| | | { |
| | | if (entry.FullName.EndsWith(".pdf")) |
| | | { |
| | | using (var stream = entry.Open()) |
| | | { |
| | | var ms = new MemoryStream(); |
| | | stream.CopyTo(ms); |
| | | ms.Position = 0; |
| | | list.Add(new NongYePayResponseZipFile |
| | | { |
| | | FileName = entry.FullName, |
| | | Stream = ms |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | return response; |
| | | private async Task<List<T>> GetList<T>(NongYePayBaseResponse<T> response, ThreeResourceLog log) |
| | | where T : class, new() |
| | | { |
| | | var list = new List<T>(); |
| | | if (response.RespSource == "0" |
| | | && response.FileFlag == "1" |
| | | && response.Cmp.BatchFileName.IsNotNull()) |
| | | { |
| | | var lines = new List<string>(); |
| | | if (options.Value.RemoteFileUrl.IsNotNull()) |
| | | { |
| | | var url = $"{options.Value.RemoteFileUrl}/api/file/download"; |
| | | var command = new DownloadFileCommand |
| | | { |
| | | Scene = "NongYePay", |
| | | Path = response.Cmp.BatchFileName |
| | | }; |
| | | var timestamp = DateTime.Now.ToTimeStamp(true); |
| | | var sign = $"POST|/api/file/download|{command.ToJson()}|{options.Value.RemoteFilePrivateKey}|{timestamp}".ToMD5Encrypt(); |
| | | var buffer = await httpRemoteService.PostAsByteArrayAsync(url, |
| | | builder => builder |
| | | .SetJsonContent(command) |
| | | .WithHeader("x-timestamp", timestamp, replace: true) |
| | | .WithHeader("x-sign", sign, replace: true)); |
| | | var text = Encoding.GetEncoding("GBK").GetString(buffer); |
| | | lines = text.Split("\n").Where(it => it.IsNotNull()).ToList(); |
| | | } |
| | | else if (options.Value.FilePath.IsNotNull()) |
| | | { |
| | | var fileName = $"{options.Value.FilePath}{response.Cmp.BatchFileName}"; |
| | | if (File.Exists(fileName)) |
| | | { |
| | | var array = await File.ReadAllLinesAsync(fileName, Encoding.GetEncoding("GBK")); |
| | | lines = array.ToList(); |
| | | } |
| | | } |
| | | if (lines.IsNotNull()) |
| | | { |
| | | var props = typeof(T).GetProperties(); |
| | | foreach (var line in lines) |
| | | { |
| | | var item = new T(); |
| | | var columns = line.Split("|"); |
| | | for (int i = 0; i < columns.Count() - 1; i++) |
| | | { |
| | | props[i].SetValue(item, columns[i]); |
| | | } |
| | | list.Add(item); |
| | | } |
| | | } |
| | | |
| | | log.UpdatedTime = DateTimeOffset.Now; |
| | | var json = list.ToJson(); |
| | | log.Response += $"\n{json}"; |
| | | await repThreeResourceLog.UpdateNowAsync(log); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private async Task<TResponse> SendWithFile<TRequest, TResponse, TResponseItem>(TRequest request) |
| | | where TRequest : NongYePayBaseRequest |
| | | where TResponse : NongYePayBaseResponse<TResponseItem>, new() |
| | | where TResponseItem : class, new() |
| | | { |
| | | var response = await SendWithLog<TRequest, TResponse>(request); |
| | | response.response.Items = await GetList(response.response, response.log); |
| | | return response.response; |
| | | } |
| | | |
| | | private async Task<TResponse> SendWithZip<TRequest, TResponse>(TRequest request) |
| | | where TRequest : NongYePayBaseRequest |
| | | where TResponse : NongYePayZipResponse, new() |
| | | { |
| | | var response = await SendWithLog<TRequest, TResponse>(request); |
| | | response.response.Items = await GetZipFiles(response.response); |
| | | return response.response; |
| | | } |
| | | |
| | | private async Task<TResponse> Send<TRequest, TResponse>(TRequest request) |
| | | where TRequest : NongYePayBaseRequest |
| | | where TResponse : NongYePayBaseResponse, new() |
| | | { |
| | | var response = await SendWithLog<TRequest, TResponse>(request); |
| | | return response.response; |
| | | } |
| | | |
| | | private async Task<(TResponse response, ThreeResourceLog log)> SendWithLog<TRequest, TResponse>(TRequest request) |
| | | where TRequest : NongYePayBaseRequest |
| | | where TResponse : NongYePayBaseResponse, new() |
| | | { |
| | | try |
| | | { |
| | | var logier = JwtUtils.GetCurrentLogier(); |
| | | var now = DateTime.Now; |
| | | request.CorpNo = options.Value.CorpNo; |
| | | request.OpNo = options.Value.OpNo; |
| | | request.AuthNo = ""; |
| | | request.Sign = ""; |
| | | request.ReqDate = now.ToString("yyyyMMdd"); |
| | | request.ReqTime = now.ToString("HHmmss"); |
| | | request.ReqSeqNo = request.ReqSeqNo ?? GetSeqNo(); |
| | | request.ChannelType = "ERP"; |
| | | request.ProductID = "ICC"; |
| | | |
| | | string requestXml = SerializeRequest(request); |
| | | var log = new ThreeResourceLog |
| | | { |
| | | CreatedTime = DateTimeOffset.Now, |
| | | Id = IDGen.NextID(), |
| | | TraceId = App.GetTraceId(), |
| | | Method = EnumResourceMethod.Post, |
| | | Domain = $"{options.Value.Ip}:{options.Value.Port}", |
| | | Path = request.CCTransCode, |
| | | CreatedUserId = logier?.Id, |
| | | CreatedChannelId = logier?.ChannelId, |
| | | Request = requestXml, |
| | | }; |
| | | await repThreeResourceLog.InsertNowAsync(log); |
| | | var stopwatch = Stopwatch.StartNew(); |
| | | var responseXml = await SendRequest(requestXml); |
| | | stopwatch.Stop(); |
| | | var response = DeserializeResponse<TResponse>(responseXml); |
| | | log.UpdatedTime = DateTimeOffset.Now; |
| | | log.Response = responseXml; |
| | | log.IsSuccess = response.RespSource == "0"; |
| | | log.ElapsedMilliseconds = stopwatch.ElapsedMilliseconds; |
| | | await repThreeResourceLog.UpdateNowAsync(log); |
| | | return (response, log); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return (new TResponse |
| | | { |
| | | RespSource = "500", |
| | | RespInfo = ex.Message |
| | | }, null); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | /// </summary> |
| | | private T DeserializeResponse<T>(string gbkXml) |
| | | { |
| | | var buffer = Encoding.GetEncoding("GBK").GetBytes(gbkXml); |
| | | using var ms = new MemoryStream(buffer); |
| | | using var sr = new StreamReader(ms, Encoding.GetEncoding("GBK")); |
| | | var serializer = new XmlSerializer(typeof(T)); |
| | | using var stream = new MemoryStream(Encoding.UTF8.GetBytes(gbkXml)); |
| | | return (T)serializer.Deserialize(stream); |
| | | using var xr = XmlReader.Create(sr); |
| | | return (T)serializer.Deserialize(xr); |
| | | } |
| | | |
| | | /// <summary> |