using Furion; using Furion.DatabaseAccessor; using Furion.DistributedIDGenerator; using Furion.DynamicApiController; using Furion.FriendlyException; using Mapster; using MediatR; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ActionConstraints; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Routing; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace FlexJobApi.Core { /// /// 资源工具 /// public static class ResourceUtils { /// /// 生成动态控制器 /// public static async Task BuildDynamicControllersAsync() { var traceId = App.GetTraceId() ?? IDGen.NextID().ToString(); var scopeFactory = App.GetService(); var serviceScope = scopeFactory.CreateScope(); var rep = serviceScope.ServiceProvider.GetRequiredService>(); var provider = serviceScope.ServiceProvider.GetRequiredService(); var xmlDoc = await XmlDocUtils.GetXmlDocAsync(); var enumWebApiMethods = await EnumUtils.GetModel(); var resourceControllers = await EnumUtils.GetModel(); var requests = App.Assemblies.SelectMany(it => it.GetTypes()).Where(it => typeof(IBaseRequest).IsAssignableFrom(it)).ToList(); var resources = await rep.AsQueryable() .Where(it => !it.IsExpired) .ToListAsync(); var models = new List(); foreach (var request in requests) { var resourceAttribute = request.GetCustomAttribute(); if (resourceAttribute == null) throw Oops.Oh(EnumErrorCodeType.s404, $"请给资源{request.Name}分配服务特性Resource"); foreach (var controller in resourceAttribute.Controllers) { var resourceController = controller.GetType().GetMember(controller.ToString())[0].GetCustomAttribute(); var resourceService = resourceController.Service.GetType().GetMember(resourceController.Service.ToString())[0].GetCustomAttribute(); var model = new ResourceModel(); model.TraceId = traceId; model.ApplicationName = resourceService.ApplicationName; model.RouteArea = resourceService.RouteArea; var name = request.Name; name = Regex.Replace(name, @"(Command|Query)$", "", RegexOptions.None); model.ActionName = name; model.Route = $"/api/{resourceService.RouteArea ?? "main"}/{controller}/{name}"; var requestXmlDoc = await request.GetXmlDocMemberAsync(xmlDoc); model.Code = requestXmlDoc.Name; model.Controller = controller; model.ControllerSummary = resourceControllers.GetDescription(controller); if (controller == EnumResourceController.Role) { Console.WriteLine(); } model.Method = request.BaseType?.IsGenericType == true && request.BaseType.GetGenericTypeDefinition() == typeof(PagedListQuery<,>) ? EnumResourceMethod.Post : new List { "Post", "Add", "Create", "Insert", "Submit" }.Any(it => request.Name.StartsWith(it, StringComparison.OrdinalIgnoreCase)) ? EnumResourceMethod.Post : new List { "GetAll", "GetList", "Get", "Find", "Fetch", "Query" }.Any(it => request.Name.StartsWith(it, StringComparison.OrdinalIgnoreCase)) ? EnumResourceMethod.Get : new List { "Put", "Update ", "Set" }.Any(it => request.Name.StartsWith(it, StringComparison.OrdinalIgnoreCase)) ? EnumResourceMethod.Put : new List { "Delete", "Remove ", "Clear" }.Any(it => request.Name.StartsWith(it, StringComparison.OrdinalIgnoreCase)) ? EnumResourceMethod.Delete : EnumResourceMethod.Post; model.Name = requestXmlDoc?.Summary; model.AllowAnonymous = resourceAttribute.AllowAnonymous; model.RequestTypeName = request.Name; model.RequestTypeFullName = request.FullName; var iRequestType = request.GetInterface("IRequest`1"); if (iRequestType != null && iRequestType.IsGenericType) { var responseType = iRequestType.GenericTypeArguments[0]; model.ResponseTypeName = responseType.GetCSharpFriendlyName(); model.ResponseTypeFullName = responseType.FullName; } var resource = resources.FirstOrDefault(it => it.Route == model.Route && it.Method == model.Method); if (resource == null) { resource = new Resource(); model.Adapt(resource); await rep.InsertAsync(resource); resources.Add(resource); } else { model.Adapt(resource); await rep.UpdateAsync(resource); } models.Add(model); } } var expiredResources = resources.Where(it => it.TraceId != traceId).ToList(); foreach (var expiredResource in expiredResources) { expiredResource.IsExpired = true; await rep.UpdateAsync(expiredResource); } var controllers = models .GroupBy(it => new { it.Controller, it.ControllerSummary, it.ApplicationName, it.RouteArea }) .Select(it => new { it.Key, Actions = it.ToList() }) .ToList(); foreach (var controller in controllers) { var code = $@" using FlexJobApi.Core; using Furion.DynamicApiController; using Furion.FriendlyException; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System; using System.Threading.Tasks; using System.Collections.Generic; using System.ComponentModel; namespace {controller.Key.ApplicationName} {{ /// /// {controller.Key.ControllerSummary} /// [Route(""api/{controller.Key.RouteArea}/[controller]"")] public class {controller.Key.Controller}AppService(IMediator mediator) : IDynamicApiController {{ private readonly IMediator mediator = mediator;"; foreach (var action in controller.Actions) { code += $@" /// /// {action.Name} /// /// /// "; if (action.AllowAnonymous) { code += $@" [AllowAnonymous]"; } code += $@" [Http{action.Method}] public Task<{action.ResponseTypeName}> {action.ActionName}({action.RequestTypeName} request) {{ return mediator.Send(request); }} "; } code += $@" }} }} "; var dynamicAssembly = App.CompileCSharpClassCode(code); provider.AddAssembliesWithNotifyChanges(dynamicAssembly); } await rep.SaveNowAsync(); } /// /// 获取C#友好名称 /// /// /// public static string GetCSharpFriendlyName(this Type type) { // 处理可空类型 if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { Type underlyingType = Nullable.GetUnderlyingType(type)!; return $"{GetCSharpFriendlyName(underlyingType)}?"; } // 处理基础类型 if (type.FullName.IsNotNull()) { var baseTypes = new Dictionary { { "System.Byte", "byte" }, { "System.SByte", "sbyte" }, { "System.Int16", "short" }, { "System.UInt16", "ushort" }, { "System.Int32", "int" }, { "System.UInt32", "uint" }, { "System.Int64", "long" }, { "System.UInt64", "ulong" }, { "System.Single", "float" }, { "System.Double", "double" }, { "System.Decimal", "decimal" }, { "System.Char", "char" }, { "System.Boolean", "bool" }, { "System.String", "string" }, { "System.Object", "object" } }; if (baseTypes.TryGetValue(type.FullName, out string? friendlyName) && friendlyName.IsNotNull()) { return friendlyName; } } // 处理非泛型类型 if (!type.IsGenericType) { return type.Name; } // 处理泛型类型 string genericTypeName = type.GetGenericTypeDefinition().Name; if (genericTypeName.Contains('`')) genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`')); string[] genericArgs = type.GetGenericArguments() .Select(GetCSharpFriendlyName) .ToArray(); return $"{genericTypeName}<{string.Join(", ", genericArgs)}>"; } } }