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 options, IRepository repThreeResourceLog ) : ITransient { private readonly IOptions options = options; private readonly IRepository repThreeResourceLog = repThreeResourceLog; /// /// 查询账户余额 /// /// 余额信息 public Task GetBalance(NongYePayGetBalanceRequest request) { return Send(request); } private async Task Send(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(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; } /// /// 泛型请求类转XML /// private string SerializeRequest(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)); } /// /// XML转泛型应答类 /// private T DeserializeResponse(string gbkXml) { var serializer = new XmlSerializer(typeof(T)); using var stream = new MemoryStream(Encoding.UTF8.GetBytes(gbkXml)); return (T)serializer.Deserialize(stream); } /// /// 发送请求并接收应答 /// private async Task 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(); } } } }