ExportDataController.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. using Hotline.Application.ExportExcel;
  2. using Hotline.Share.Attributes;
  3. using Hotline.Share.Dtos.Order;
  4. using Hotline.Share.Tools;
  5. using Microsoft.AspNetCore.Mvc;
  6. using SqlSugar;
  7. using System.ComponentModel;
  8. using System.Reflection;
  9. using XF.Domain.Exceptions;
  10. namespace Hotline.Api.Controllers.ExportData;
  11. /// <summary>
  12. /// 只要URL结尾是 export_excel 的请求都会进入这个控制器
  13. /// 逻辑:
  14. /// 1. 通过URL找到对应的Controller和Action
  15. /// 2. 通过Controller和Action找到对应的ApplicationService
  16. /// 比如: RedPackController.GetRedPackList 就会去查找 RedPackApplication.GetRedPackList 方法
  17. /// 3. 通过反射调用对应的方法
  18. /// 4. ApplicationService 的方法返回的必须是 ISugarQueryable
  19. /// 5. ApplicationService 中方法的ExportExcel属性会作为导出的文件名
  20. /// </summary>
  21. [ApiController]
  22. [Route("{*path:regex(.*export_excel$)}")]
  23. public class ExportDataController : BaseController
  24. {
  25. private readonly IServiceProvider _serviceProvider;
  26. private readonly EndpointDataSource _endpointDataSource;
  27. private readonly IExportApplication _exportApplication;
  28. public ExportDataController(IServiceProvider serviceProvider, EndpointDataSource endpointDataSource, IExportApplication exportApplication)
  29. {
  30. _serviceProvider = serviceProvider;
  31. _endpointDataSource = endpointDataSource;
  32. _exportApplication = exportApplication;
  33. }
  34. /// <summary>
  35. /// 动态导出数据
  36. /// </summary>
  37. /// <returns></returns>
  38. [HttpPost, HttpGet]
  39. public async Task<FileStreamResult> HandleExportPost()
  40. {
  41. var fullPath = HttpContext.Request.Path.Value;
  42. var originalPath = fullPath?.Substring(0, fullPath.LastIndexOf("/export_excel")).TrimStart('/');
  43. if (string.IsNullOrEmpty(originalPath))
  44. throw UserFriendlyException.SameMessage("无效的URL地址" + fullPath);
  45. var matchingEndpoints = _endpointDataSource.Endpoints
  46. .OfType<RouteEndpoint>()
  47. .Where(endpoint => endpoint.RoutePattern.RawText == originalPath)
  48. .ToList()
  49. ?? throw UserFriendlyException.SameMessage($"根据URL查询路由失败: {originalPath}");
  50. var matchingEndpoint = matchingEndpoints.First();
  51. if (matchingEndpoints.Count > 1)
  52. {
  53. matchingEndpoint = matchingEndpoints.First(m => m.DisplayName.Contains("Get"));
  54. }
  55. if (matchingEndpoint == null)
  56. throw UserFriendlyException.SameMessage($"根据URL查询路由失败: {originalPath}");
  57. var controllerName = matchingEndpoint.RoutePattern.RequiredValues["controller"]?.ToString();
  58. var actionName = matchingEndpoint.RoutePattern.RequiredValues["action"]?.ToString();
  59. var applicationServiceType = GetApplicationServiceType(controllerName)
  60. ?? throw UserFriendlyException.SameMessage($"根据名称查找方法失败. '{controllerName}'");
  61. var method = applicationServiceType.GetMethod(actionName);
  62. if (method == null)
  63. {
  64. method = applicationServiceType.GetMethod(actionName + "Async");
  65. if (method == null)
  66. throw UserFriendlyException.SameMessage($"根据方法名查找方法失败. '{actionName}'");
  67. }
  68. var serviceInstance = _serviceProvider.GetService(applicationServiceType);
  69. if (serviceInstance == null)
  70. {
  71. serviceInstance = _serviceProvider.GetService(applicationServiceType.GetInterfaces()[0]);
  72. if (serviceInstance == null)
  73. throw UserFriendlyException.SameMessage($"获取注入失败. '{applicationServiceType.Name}'.");
  74. }
  75. var parameters = method.GetParameters();
  76. using var reader = new StreamReader(HttpContext.Request.Body);
  77. var body = await reader.ReadToEndAsync();
  78. var param = parameters[0];
  79. var genericType = typeof(ExportExcelDto<>).MakeGenericType(param.ParameterType);
  80. var exportData = body.FromJson(genericType);
  81. var queryDto = genericType.GetProperty("QueryDto")?.GetValue(exportData);
  82. var isExportAll = genericType.GetProperty("IsExportAll")?.GetValue(exportData);
  83. var pageIndex = param.ParameterType.GetProperty("PageIndex")?.GetValue(queryDto);
  84. var pageSize = param.ParameterType.GetProperty("PageSize")?.GetValue(queryDto);
  85. var result = method.Invoke(serviceInstance, [queryDto]);
  86. var returnType = method.ReturnType.GetGenericArguments()[0];
  87. var fileName = method.GetFileName() + "_";
  88. if (pageIndex == null || pageSize == null)
  89. {
  90. isExportAll = true;
  91. pageIndex = 1;
  92. pageSize = 20;
  93. }
  94. return _exportApplication.GetExcelFile(returnType, genericType, exportData, ConvertToList(result, (bool)isExportAll, (int)pageIndex, (int)pageSize), fileName);
  95. }
  96. public static List<object>? ConvertToList(object? result, bool isExportAll, int pageIndex, int pageSize)
  97. {
  98. if (result == null)
  99. {
  100. return null;
  101. }
  102. var type = result.GetType();
  103. if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(PostgreSQLQueryable<>))
  104. {
  105. throw UserFriendlyException.SameMessage("被导出方法的返回类型不是 ISugarQueryable");
  106. }
  107. var genericArgument = type.GetGenericArguments()[0];
  108. if (isExportAll)
  109. {
  110. var toListMethod = type.GetMethods()
  111. .Where(m => m.Name == "ToList" && m.GetParameters().Length == 0)
  112. .First();
  113. var list = toListMethod.Invoke(result, null) as IEnumerable<object>;
  114. return list?.ToList();
  115. }
  116. else
  117. {
  118. var toListMethod = type.GetMethods()
  119. .Where(m => m.Name == "ToPageList" && m.GetParameters().Length == 2)
  120. .First();
  121. var list = toListMethod.Invoke(result, [pageIndex, pageSize]) as IEnumerable<object>;
  122. return list?.ToList();
  123. }
  124. }
  125. private Type GetApplicationServiceType(string controllerName)
  126. {
  127. var name = controllerName + "Application";
  128. var type = AppDomain.CurrentDomain.GetAssemblies().ToList()
  129. .SelectMany(d => d.GetTypes())
  130. .Where(d => d.Name == name)
  131. .First();
  132. return type;
  133. }
  134. }