using Aop.Api.Domain;
|
using ApiTools.Core.Utils.NongYePayUtils.Models;
|
using Azure;
|
using Azure.Core;
|
using Furion;
|
using Furion.DatabaseAccessor;
|
using Furion.DependencyInjection;
|
using Furion.DistributedIDGenerator;
|
using Furion.FriendlyException;
|
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;
|
using System.Threading.Tasks;
|
using System.Xml;
|
using System.Xml.Serialization;
|
|
namespace ApiTools.Core
|
{
|
public class NongYePayUtils(
|
IOptions<NongYePayOptions> options,
|
IRepository<ThreeResourceLog, LogDbContextLocator> repThreeResourceLog
|
) : ITransient
|
{
|
private readonly IOptions<NongYePayOptions> options = options;
|
private readonly IRepository<ThreeResourceLog, LogDbContextLocator> repThreeResourceLog = repThreeResourceLog;
|
|
/// <summary>
|
/// 查询账户余额
|
/// </summary>
|
/// <returns>余额信息</returns>
|
public Task<NongYePayGetBalanceResponse> GetBalance(NongYePayGetBalanceRequest request)
|
{
|
return Send<NongYePayGetBalanceRequest, NongYePayGetBalanceResponse>(request);
|
}
|
|
/// <summary>
|
/// 单笔对公转账
|
/// </summary>
|
/// <param name="request"></param>
|
/// <returns></returns>
|
public Task<NongYePaySingleCorporateTransferResponse> SingleCorporateTransfer(NongYePaySingleCorporateTransferRequest request)
|
{
|
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)
|
{
|
var response = await Send<NongYePayDownloadEreceiptRequest, NongYePayDownloadEreceiptResponse>(request);
|
if (response != null
|
&& response.RespSource == "0"
|
&& response.Cmp != null
|
&& response.Cmp.BatchFileName.IsNotNull())
|
{
|
response.ZipFileName = $"{options.Value.FilePath}{response.Cmp.BatchFileName}";
|
if (File.Exists(response.ZipFileName))
|
{
|
using (var archive = ZipFile.OpenRead(response.ZipFileName))
|
{
|
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;
|
response.Items.Add(new NongYePayDownloadEreceiptResponseItem
|
{
|
FileName = entry.FullName,
|
Stream = ms
|
});
|
}
|
}
|
}
|
}
|
}
|
}
|
return response;
|
}
|
|
public async Task<NongYePayRealTimeDownloadEreceiptResponse> RealTimeDownloadEreceipt(NongYePayRealTimeDownloadEreceiptRequest request)
|
{
|
var response = await Send<NongYePayRealTimeDownloadEreceiptRequest, NongYePayRealTimeDownloadEreceiptResponse>(request);
|
if (response != null
|
&& response.RespSource == "0"
|
&& response.Cmp != null
|
&& response.Cmp.BatchFileName.IsNotNull())
|
{
|
response.ZipFileName = $"{options.Value.FilePath}{response.Cmp.BatchFileName}";
|
if (File.Exists(response.ZipFileName))
|
{
|
using (var archive = ZipFile.OpenRead(response.ZipFileName))
|
{
|
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;
|
response.Items.Add(new NongYePayDownloadEreceiptResponseItem
|
{
|
FileName = entry.FullName,
|
Stream = ms
|
});
|
}
|
}
|
}
|
}
|
}
|
}
|
return response;
|
}
|
|
/// <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);
|
return $"{now:yyyyMMddHHmmssfff}{random}";
|
}
|
|
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 fileName = $"{options.Value.FilePath}{response.Cmp.BatchFileName}";
|
if (File.Exists(fileName))
|
{
|
var props = typeof(T).GetProperties();
|
var lines = await File.ReadAllLinesAsync(fileName, Encoding.GetEncoding("GBK"));
|
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<TResponseItem>(response.response, response.log);
|
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>
|
/// 泛型请求类转XML
|
/// </summary>
|
private string SerializeRequest<T>(T request)
|
{
|
var serializer = new XmlSerializer(typeof(T));
|
var ns = new XmlSerializerNamespaces();
|
ns.Add("", "");
|
|
var settings = new XmlWriterSettings
|
{
|
OmitXmlDeclaration = true,
|
Indent = false,
|
Encoding = Encoding.UTF8
|
};
|
|
using var stream = new MemoryStream();
|
using var writer = XmlWriter.Create(stream, settings);
|
serializer.Serialize(writer, request, ns);
|
|
// 转换为GBK编码
|
stream.Position = 0;
|
using var reader = new StreamReader(stream, Encoding.UTF8);
|
string utf8Xml = reader.ReadToEnd();
|
return Encoding.GetEncoding("GBK").GetString(Encoding.GetEncoding("GBK").GetBytes(utf8Xml));
|
}
|
|
/// <summary>
|
/// XML转泛型应答类
|
/// </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 xr = XmlReader.Create(sr);
|
return (T)serializer.Deserialize(xr);
|
}
|
|
/// <summary>
|
/// 发送请求并接收应答
|
/// </summary>
|
private async Task<string> SendRequest(string requestXml)
|
{
|
byte[] requestBytes = Encoding.GetEncoding("GBK").GetBytes(requestXml);
|
|
// 2. 构建7字节包头(加密标志0 + 数据长度,右补空格)
|
byte[] header = new byte[7];
|
header[0] = 0x30; // 加密标志:0(ASCII码)
|
string requestLengthStr = requestBytes.Length.ToString().PadRight(6, ' '); // 后6字节:长度右补空格
|
Buffer.BlockCopy(Encoding.ASCII.GetBytes(requestLengthStr), 0, header, 1, 6);
|
|
// 3. 合并包头+数据包
|
byte[] sendData = new byte[header.Length + requestBytes.Length];
|
Buffer.BlockCopy(header, 0, sendData, 0, header.Length);
|
Buffer.BlockCopy(requestBytes, 0, sendData, header.Length, requestBytes.Length);
|
|
// 4. Socket发送(带超时)
|
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
socket.SendTimeout = (int)TimeSpan.FromMinutes(5).TotalMilliseconds;
|
socket.ReceiveTimeout = (int)TimeSpan.FromMinutes(5).TotalMilliseconds;
|
|
try
|
{
|
await socket.ConnectAsync(options.Value.Ip, options.Value.Port);
|
await socket.SendAsync(sendData);
|
|
// 接收包头
|
byte[] responseHeader = new byte[7];
|
int headerLen = socket.Receive(responseHeader);
|
if (headerLen != 7) throw Oops.Oh(EnumErrorCodeType.s510, "接收包头失败");
|
|
// 解析数据长度
|
string lengthStr = Encoding.ASCII.GetString(responseHeader, 1, 6).Trim();
|
if (!int.TryParse(lengthStr, out int dataLength))
|
throw Oops.Oh(EnumErrorCodeType.s510, $"解析数据长度失败:{lengthStr}");
|
|
// 接收数据包
|
byte[] responseBytes = new byte[dataLength];
|
int receivedLen = 0;
|
while (receivedLen < dataLength)
|
{
|
int len = socket.Receive(responseBytes, receivedLen, dataLength - receivedLen, SocketFlags.None);
|
receivedLen += len;
|
}
|
|
// 转换为GBK XML
|
var responseXml = Encoding.GetEncoding("GBK").GetString(responseBytes);
|
if (responseXml.IsNull())
|
{
|
throw Oops.Oh(EnumErrorCodeType.s510, "未收到应答");
|
}
|
return responseXml;
|
}
|
finally
|
{
|
socket.Dispose();
|
}
|
}
|
}
|
}
|