admin/src/SimpleAdmin.Api/SimpleAdminApiModule.cs
zzh 41f4c58e99 Improve draft predeclare billing and structured create-fail payload.
Re-run forecast billing on draft pre-declare, and expose draftSaved/orderId/serviceNo via ErrorCode/Data/details for clearer place-order failure UX.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 19:30:27 +08:00

156 lines
5.4 KiB
C#

using Fake.AspNetCore;
using Fake.Autofac;
using Fake.Modularity;
using Fake.Rbac.Application;
using Fake.Rbac.Infrastructure;
using Fake.AspNetCore.Auditing;
using Fake.AspNetCore.ExceptionHandling;
using Fake.AspNetCore.Newtonsoft;
using Fake.AspNetCore.Swagger;
using Fake.FeiShu;
using Fake.Security.Claims;
using Fake.Serilog;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.DependencyInjection;
using Oms.Application;
using Oms.SqlSugarCore;
using SimpleAdmin.Api.Auth;
using SimpleAdmin.Api.Swagger;
using SimpleAdmin.Api.WeChat;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace SimpleAdmin.Api;
[DependsOn(
typeof(FakeAutofacModule),
typeof(OmsApplicationModule),
typeof(OmsSqlSugarCoreModule),
typeof(FakeRbacApplicationModule),
typeof(FakeRbacInfrastructureModule),
typeof(FakeAspNetCoreModule),
typeof(FakeSerilogModule)
)]
public class SimpleAdminApiModule : FakeModule
{
private const string DefaultCorsPolicyName = "default";
public override void PreConfigureServices(ServiceConfigurationContext context)
{
FakeClaimTypes.UserId = "mapId";
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
var configuration = context.Services.GetConfiguration();
// 配置动态 API - 扫描 OMS 与 RBAC 应用服务
services.Configure<FakeAspNetCoreMvcOptions>(options =>
{
options.ApplicationServices2Controller<OmsApplicationModule>();
options.ApplicationServices2Controller<SimpleAdminApiModule>(settings =>
{
settings.RootPath = "rbac";
settings.TypePredicate = type =>
type == typeof(AuthService) || type == typeof(WeChatCustomerService);
});
options.ApplicationServices2Controller<FakeRbacApplicationModule>(settings =>
{
settings.RootPath = "rbac";
settings.TypePredicate = type =>
type != typeof(Fake.Rbac.Application.Services.AuthService) &&
type != typeof(Fake.Rbac.Application.Services.UserService);
});
});
services.AddOmsWeChat(configuration);
services.AddSingleton<IRbacAuthCache, RbacAuthCache>();
//Json格式化全局处理
context.Services.AddFakeNewtonsoft();
services.AddFakeAspNetCoreAuditing();
// 添加 Fake Swagger 支持
services.AddFakeSwaggerGen(addSecurity: true);
// Fake.Rbac 菜单/组织/权限/角色接口无 XML 注释,补全 summary
services.Configure<SwaggerGenOptions>(options =>
{
options.OperationFilter<RbacSwaggerOperationFilter>();
});
services.AddCors(options =>
{
options.AddPolicy(DefaultCorsPolicyName, builder =>
{
builder.WithOrigins(configuration.GetSection("App:CorsOrigins").Get<string[]>() ?? [])
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
services.Configure<ForwardedHeadersOptions>(options =>
{
// 反向代理场景下恢复客户端真实来源信息,供 Swagger、鉴权和重定向等中间件使用
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto |
ForwardedHeaders.XForwardedHost;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
services.AddFeiShuExceptionSubscriber();
// 草稿已落库失败:结构化 Details + 清理 msg 机器标记
services.AddTransient<DefaultException2ResponseConverter>();
var exceptionConverterDescriptors = services
.Where(d => d.ServiceType == typeof(IException2ResponseConverter))
.ToList();
foreach (var descriptor in exceptionConverterDescriptors)
{
services.Remove(descriptor);
}
services.AddTransient<IException2ResponseConverter>(sp =>
new ExceptionHandling.OmsException2ResponseConverter(
sp.GetRequiredService<DefaultException2ResponseConverter>()));
}
public override void ConfigureApplication(ApplicationConfigureContext context)
{
var app = context.GetWebApplication();
// 必须在读取 Scheme/Host/PathBase 的中间件之前执行,否则 Swagger 会生成错误的请求地址
app.UseForwardedHeaders();
// 虚拟路由-网关适配
app.UsePathBase(app.Configuration["ASPNETCORE_PATHBASE"]);
// Swagger
app.UseFakeSwagger();
// 异常处理
app.UseFakeExceptionHandling();
// CORS
app.UseCors(DefaultCorsPolicyName);
// 静态文件服务(用于头像等上传文件)
app.UseStaticFiles();
// 路由 - 必须在认证和授权之前
app.UseRouting();
// 认证和授权
app.UseAuthentication();
app.UseAuthorization();
// 角色/菜单写操作后失效登录用户信息缓存
app.UseMiddleware<RbacCacheInvalidationMiddleware>();
// 端点映射
app.MapControllers();
app.Services.GetRequiredService<IFeiShuNotificationService>().Send("Application started");
}
}