using Aop.Api.Domain;
|
using ApiTools.Core.Utils.NongYePayUtils.Models;
|
using Furion;
|
using Furion.DatabaseAccessor;
|
using Furion.DependencyInjection;
|
using Furion.DistributedIDGenerator;
|
using Furion.FriendlyException;
|
using Microsoft.Extensions.Options;
|
using NetTopologySuite.Algorithm;
|
using System;
|
using System.Collections.Generic;
|
using System.Diagnostics;
|
using System.IO;
|
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);
|
}
|
|
private async Task<TResponse> Send<TRequest, TResponse>(TRequest request)
|
where TRequest : NongYePayBaseRequest
|
where TResponse : NongYePayBaseResponse
|
{
|
var logier = JwtUtils.GetCurrentLogier();
|
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";
|
|
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.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);
|
|
if (!log.IsSuccess) throw Oops.Oh(EnumErrorCodeType.s510, $"农行接口异常:{response.RespInfo}");
|
|
return response;
|
}
|
|
/// <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 serializer = new XmlSerializer(typeof(T));
|
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(gbkXml));
|
return (T)serializer.Deserialize(stream);
|
}
|
|
/// <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();
|
}
|
}
|
}
|
}
|