using OASystem.Domain.AesEncryption; using OASystem.Domain.Attributes; using OASystem.Domain.Entities.Customer; using Org.BouncyCastle.Asn1.Pkcs; using System.Net; using UAParser; using static OASystem.API.OAMethodLib.JWTHelper; namespace OASystem.API.Middlewares { /// /// 指定API操作记录信息 /// public class RecordAPIOperationMiddleware { private readonly RequestDelegate _next; private readonly HttpClient _httpClient; private readonly IConfiguration _config; private readonly SqlSugarClient _sqlSugar; /// /// 初始化 /// /// /// /// /// public RecordAPIOperationMiddleware(RequestDelegate next, IConfiguration config, HttpClient httpClient, SqlSugarClient sqlSugar) { _next = next; _httpClient = httpClient; _config = config; _sqlSugar = sqlSugar; } public async Task InvokeAsync(HttpContext context) { // 启用请求体流的缓冲,允许多次读取 context.Request.EnableBuffering(); // 读取请求体内容 var requestBodyText = await ReadRequestBody(context.Request); // 检查控制器方法是否使用了自定义属性 var endpoint = context.GetEndpoint(); var apiLogAttribute = endpoint?.Metadata?.GetMetadata(); if (apiLogAttribute != null) { var startTime = DateTime.UtcNow; int portType = 1, userId = 0, id = 0, status = 0; string updatePreData = string.Empty, updateBefData = string.Empty; bool param5Bool = false, param6Bool = false; try { userId = await ReadToken(context); if (!string.IsNullOrEmpty(requestBodyText)) { object param1 = string.Empty, param2 = string.Empty, param3 = string.Empty, param4 = string.Empty, param5 = string.Empty, param6 = string.Empty, param7 = string.Empty, param8 = string.Empty; var requestBodyJson = JsonConvert.DeserializeObject>(requestBodyText); bool exists1 = requestBodyJson.TryGetValue("portType", out param1); bool exists2 = requestBodyJson.TryGetValue("userId", out param2); bool exists3 = requestBodyJson.TryGetValue("currUserId", out param3); bool exists4 = requestBodyJson.TryGetValue("createUserId", out param4); bool exists5 = requestBodyJson.TryGetValue("id", out param5); bool exists6 = requestBodyJson.TryGetValue("status", out param6); bool exists7 = requestBodyJson.TryGetValue("operationUserId", out param7); bool exists8 = requestBodyJson.TryGetValue("deleteUserId", out param8); if (exists1) int.TryParse(param1.ToString(), out portType); //用户Id处理 if (userId < 1) { if (apiLogAttribute.OperationEnum == OperationEnum.Login) { var number = requestBodyJson?["number"].ToString(); if (!string.IsNullOrEmpty(number)) { var info = await _sqlSugar.Queryable().Where(x => x.IsDel == 0 && x.Number.Equals(number)).FirstAsync(); userId = info.Id; } } else { if (exists2) int.TryParse(param2.ToString(), out userId); else if (exists3) int.TryParse(param3.ToString(), out userId); else if (exists4) int.TryParse(param4.ToString(), out userId); else if (exists7) int.TryParse(param7.ToString(), out userId); else if (exists8) int.TryParse(param8.ToString(), out userId); } } //编辑前数据查询; if (exists5) param5Bool = int.TryParse(param5.ToString(), out id); if (exists6) param6Bool = int.TryParse(param6.ToString(), out status); if (param6Bool) { // 2 修改 if (status == 1) apiLogAttribute.OperationEnum = OperationEnum.Add; else if (status == 2) { apiLogAttribute.OperationEnum = OperationEnum.Edit; updatePreData = await TableInfoToJson(apiLogAttribute.TableName, id); } } else if (param5Bool) { if (apiLogAttribute.OperationEnum != OperationEnum.Del) { // id > 0 修改 if (id < 1) apiLogAttribute.OperationEnum = OperationEnum.Add; else if (id > 0) { apiLogAttribute.OperationEnum = OperationEnum.Edit; updatePreData = await TableInfoToJson(apiLogAttribute.TableName, id); } } } } } catch (JsonException) { } // 保存原始响应体流 var originalResponseBody = context.Response.Body; // 创建一个新的内存流来捕获响应体 using var responseMemoryStream = new MemoryStream(); context.Response.Body = responseMemoryStream; // 调用下一个中间件 await _next(context); // 重置响应体流的位置 responseMemoryStream.Position = 0; // 读取响应体内容 var responseBodyText = await ReadResponseBody(responseMemoryStream); // 将响应体内容写回原始响应体流 await responseMemoryStream.CopyToAsync(originalResponseBody); //修改后数据查询 if (param6Bool) { // 2 修改 if (status == 2) updateBefData = await TableInfoToJson(apiLogAttribute.TableName, id); } else if (param5Bool) { if (apiLogAttribute.OperationEnum != OperationEnum.Del) { // id > 0 修改 if (id > 0) updateBefData = await TableInfoToJson(apiLogAttribute.TableName, id); } } string remoteIp = string.Empty, location = string.Empty; // 检查请求头中的X-Forwarded-For,以获取真实的客户端IP地址 if (context.Request.Headers.ContainsKey("X-Forwarded-For")) { remoteIp = context.Request.Headers["X-Forwarded-For"].ToString().Split(',', StringSplitOptions.RemoveEmptyEntries)[0]; //_logger.LogInformation($"IP:{remoteIp}"); } else { remoteIp = context.Connection.RemoteIpAddress?.ToString(); } (remoteIp, location) = await GetIpInfo(remoteIp); //remoteIp = await GetExternalIp(); //location = await GetIpLocation(remoteIp); string deviceType = string.Empty, browser = string.Empty, os = string.Empty; var userAgent = context.Request.Headers["User-Agent"].FirstOrDefault(); if (!string.IsNullOrEmpty(userAgent)) { // 解析User-Agent头 var parser = Parser.GetDefault(); var client = parser.Parse(userAgent); // 提取浏览器信息 browser = client.UA.Family; // 浏览器名称 var browserVersion = client.UA.Major + "." + client.UA.Minor + "." + client.UA.Patch; // 浏览器版本 browser += $"({browserVersion})"; // 提取操作系统信息 os = client.OS.Family; // 操作系统名称 var osVersion = string.Empty; // 操作系统版本 if (!string.IsNullOrEmpty(client.OS.Major)) osVersion += client.OS.Major; if (!string.IsNullOrEmpty(client.OS.Minor)) osVersion += "." + client.OS.Minor; if (!string.IsNullOrEmpty(client.OS.Patch)) osVersion += "." + client.OS.Patch; if (!string.IsNullOrEmpty(osVersion)) os += $"({osVersion})"; // 提取设备信息 deviceType = client.Device.Family; // 设备类型,如 'mobile', 'tablet', 'desktop' 等 } // 记录请求结束时间 var endTime = DateTime.UtcNow; // 计算耗时 var duration = (long)(endTime - startTime).TotalMilliseconds; var logInfo = new Crm_TableOperationRecord() { TableName = apiLogAttribute.TableName, PortType = portType, OperationItem = apiLogAttribute.OperationEnum, DataId = id, RequestUrl = context.Request.Path, RemoteIp = remoteIp, Location = location, RequestParam = !string.IsNullOrEmpty(requestBodyText) ? JsonConvert.SerializeObject(requestBodyText) : null, ReturnResult = !string.IsNullOrEmpty(responseBodyText) ? JsonConvert.SerializeObject(responseBodyText) : null, Elapsed = duration, Status = context.Response.StatusCode.ToString(), CreateUserId = userId, UpdatePreData = updatePreData, UpdateBefData = updateBefData, Browser = browser, Os = os, DeviceType = deviceType, }; // 存储到数据库 await _sqlSugar.Insertable(logInfo).ExecuteCommandAsync(); } else { await _next(context); } } private async Task TableInfoToJson(string tableName, int id) { if (_sqlSugar.DbMaintenance.IsAnyTable(tableName)) { string jsonLabel = string.Empty; if (tableName.Equals("Crm_NewClientData")) { var info = await _sqlSugar.Queryable().FirstAsync(x => x.Id == id); if (info != null) { EncryptionProcessor.DecryptProperties(info); if (info != null) jsonLabel = JsonConvert.SerializeObject(info); } } else { var sql = string.Format(" Select * From {0} Where Id={1}", tableName, id); var info = await _sqlSugar.SqlQueryable(sql).FirstAsync(); if (info != null) jsonLabel = JsonConvert.SerializeObject(info); } return jsonLabel; } return string.Empty; } private async Task ReadRequestBody(HttpRequest request) { request.EnableBuffering(); using var reader = new StreamReader( request.Body, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: 8192, leaveOpen: true ); var body = await reader.ReadToEndAsync(); request.Body.Position = 0; return body; } private async Task ReadResponseBody(Stream stream) { using var reader = new StreamReader( stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: 8192, leaveOpen: true ); var body = await reader.ReadToEndAsync(); stream.Position = 0; return body; } private async Task ReadToken(HttpContext context) { var authHeader = context.Request.Headers["Authorization"].FirstOrDefault(); // 检查Authorization头是否存在且以"Bearer "开头 if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { var authInfo = JwtHelper.SerializeJwt(authHeader); if (authInfo != null) return authInfo.UserId; } return 0; } /// /// 获取IP信息 /// /// ipv4 or ipv6 /// private async Task<(string ip, string local)> GetIpInfo(string ip) { string local = string.Empty; if (IPAddress.TryParse(ip,out _)) { var response = await _httpClient.GetAsync($"https://api.vore.top/api/IPdata?ip={ip}"); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(); var ipInfo = Newtonsoft.Json.JsonConvert.DeserializeObject(json); if (ipInfo.code == 200) { ip = ipInfo.ipinfo.text; local = $"{ipInfo.adcode.o}"; } } return (ip, local); } private async Task GetExternalIp() { var response = await _httpClient.GetAsync("https://ifconfig.me"); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } private async Task GetIpLocation(string ip) { var response = await _httpClient.GetAsync($"https://ipinfo.io/{ip}/json?&language=zh-CN"); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(); var ipInfo = Newtonsoft.Json.JsonConvert.DeserializeObject(json); return $"{ipInfo.country}, {ipInfo.city}"; } } }