123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- using Hotline.Application.ExportExcel;
- using Hotline.Share.Attributes;
- using Hotline.Share.Dtos.Order;
- using Hotline.Share.Tools;
- using Microsoft.AspNetCore.Mvc;
- using SqlSugar;
- using System.ComponentModel;
- using System.Reflection;
- using XF.Domain.Exceptions;
- namespace Hotline.Api.Controllers.ExportData;
- /// <summary>
- /// 只要URL结尾是 export_excel 的请求都会进入这个控制器
- /// 逻辑:
- /// 1. 通过URL找到对应的Controller和Action
- /// 2. 通过Controller和Action找到对应的ApplicationService
- /// 比如: RedPackController.GetRedPackList 就会去查找 RedPackApplication.GetRedPackList 方法
- /// 3. 通过反射调用对应的方法
- /// 4. ApplicationService 的方法返回的必须是 ISugarQueryable
- /// 5. ApplicationService 中方法的ExportExcel属性会作为导出的文件名
- /// </summary>
- [ApiController]
- [Route("{*path:regex(.*export_excel$)}")]
- public class ExportDataController : BaseController
- {
- private readonly IServiceProvider _serviceProvider;
- private readonly EndpointDataSource _endpointDataSource;
- private readonly IExportApplication _exportApplication;
- public ExportDataController(IServiceProvider serviceProvider, EndpointDataSource endpointDataSource, IExportApplication exportApplication)
- {
- _serviceProvider = serviceProvider;
- _endpointDataSource = endpointDataSource;
- _exportApplication = exportApplication;
- }
- /// <summary>
- /// 动态导出数据
- /// </summary>
- /// <returns></returns>
- [HttpPost, HttpGet]
- public async Task<FileStreamResult> HandleExportPost()
- {
- var fullPath = HttpContext.Request.Path.Value;
- var originalPath = fullPath?.Substring(0, fullPath.LastIndexOf("/export_excel")).TrimStart('/');
- if (string.IsNullOrEmpty(originalPath))
- throw UserFriendlyException.SameMessage("无效的URL地址" + fullPath);
- var matchingEndpoints = _endpointDataSource.Endpoints
- .OfType<RouteEndpoint>()
- .Where(endpoint => endpoint.RoutePattern.RawText == originalPath)
- .ToList()
- ?? throw UserFriendlyException.SameMessage($"根据URL查询路由失败: {originalPath}");
- var matchingEndpoint = matchingEndpoints.First();
- if (matchingEndpoints.Count > 1)
- {
- matchingEndpoint = matchingEndpoints.First(m => m.DisplayName.Contains("Get"));
- }
- if (matchingEndpoint == null)
- throw UserFriendlyException.SameMessage($"根据URL查询路由失败: {originalPath}");
- var controllerName = matchingEndpoint.RoutePattern.RequiredValues["controller"]?.ToString();
- var actionName = matchingEndpoint.RoutePattern.RequiredValues["action"]?.ToString();
- var applicationServiceType = GetApplicationServiceType(controllerName)
- ?? throw UserFriendlyException.SameMessage($"根据名称查找方法失败. '{controllerName}'");
- var method = applicationServiceType.GetMethod(actionName);
- if (method == null)
- {
- method = applicationServiceType.GetMethod(actionName + "Async");
- if (method == null)
- throw UserFriendlyException.SameMessage($"根据方法名查找方法失败. '{actionName}'");
- }
- var serviceInstance = _serviceProvider.GetService(applicationServiceType);
- if (serviceInstance == null)
- {
- serviceInstance = _serviceProvider.GetService(applicationServiceType.GetInterfaces()[0]);
- if (serviceInstance == null)
- throw UserFriendlyException.SameMessage($"获取注入失败. '{applicationServiceType.Name}'.");
- }
- var parameters = method.GetParameters();
- using var reader = new StreamReader(HttpContext.Request.Body);
- var body = await reader.ReadToEndAsync();
- var param = parameters[0];
- var genericType = typeof(ExportExcelDto<>).MakeGenericType(param.ParameterType);
- var exportData = body.FromJson(genericType);
- var queryDto = genericType.GetProperty("QueryDto")?.GetValue(exportData);
- var isExportAll = genericType.GetProperty("IsExportAll")?.GetValue(exportData);
- var pageIndex = param.ParameterType.GetProperty("PageIndex")?.GetValue(queryDto);
- var pageSize = param.ParameterType.GetProperty("PageSize")?.GetValue(queryDto);
- var result = method.Invoke(serviceInstance, [queryDto]);
- var returnType = method.ReturnType.GetGenericArguments()[0];
- var fileName = method.GetFileName() + "_";
- if (pageIndex == null || pageSize == null)
- {
- isExportAll = true;
- pageIndex = 1;
- pageSize = 20;
- }
- return _exportApplication.GetExcelFile(returnType, genericType, exportData, ConvertToList(result, (bool)isExportAll, (int)pageIndex, (int)pageSize), fileName);
- }
- public static List<object>? ConvertToList(object? result, bool isExportAll, int pageIndex, int pageSize)
- {
- if (result == null)
- {
- return null;
- }
- var type = result.GetType();
- if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(PostgreSQLQueryable<>))
- {
- throw UserFriendlyException.SameMessage("被导出方法的返回类型不是 ISugarQueryable");
- }
- var genericArgument = type.GetGenericArguments()[0];
- if (isExportAll)
- {
- var toListMethod = type.GetMethods()
- .Where(m => m.Name == "ToList" && m.GetParameters().Length == 0)
- .First();
- var list = toListMethod.Invoke(result, null) as IEnumerable<object>;
- return list?.ToList();
- }
- else
- {
- var toListMethod = type.GetMethods()
- .Where(m => m.Name == "ToPageList" && m.GetParameters().Length == 2)
- .First();
- var list = toListMethod.Invoke(result, [pageIndex, pageSize]) as IEnumerable<object>;
- return list?.ToList();
- }
- }
- private Type GetApplicationServiceType(string controllerName)
- {
- var name = controllerName + "Application";
- var type = AppDomain.CurrentDomain.GetAssemblies().ToList()
- .SelectMany(d => d.GetTypes())
- .Where(d => d.Name == name)
- .First();
- return type;
- }
- }
|