using FlexJobApi.User.Application;
using Furion;
using Furion.DatabaseAccessor;
using Furion.DistributedIDGenerator;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using MediatR;
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.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;
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)}>";
}
}
}