Files
f9-web-ai-test/backend/F9WebAiTest.Api/Program.cs
T
2026-10-08 10:43:59 +08:00

113 lines
4.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Text;
using FreeSql;
using F9WebAiTest.Api.Hubs;
using F9WebAiTest.Api.Services;
using F9WebAiTest.Application;
using F9WebAiTest.Application.Services;
using F9WebAiTest.Application.Services.Notice;
using F9WebAiTest.Infrastructure;
using F9WebAiTest.Infrastructure.Data;
using MediatR;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.SignalR;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
// 分层注册
builder.Services.AddInfrastructure(builder.Configuration);
builder.Services.AddApplication();
// JWT 认证
var jwtKey = builder.Configuration["Jwt:Key"];
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey))
};
// SignalR 通过 query string 传递 token,需从 access_token 读取
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
if (!string.IsNullOrEmpty(accessToken) && context.HttpContext.Request.Path.StartsWithSegments("/hubs"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
builder.Services.AddAuthorization();
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
// long 序列化为字符串,避免雪花 ID 超出 JS 安全整数范围导致精度丢失
options.JsonSerializerOptions.Converters.Add(new F9WebAiTest.Api.JsonConverters.LongToStringJsonConverter());
});
// SignalR 实时推送 + 自定义用户标识 + 即时推送器(创建消息时直接推送,无后台投递服务)
builder.Services.AddSignalR();
builder.Services.AddSingleton<IUserIdProvider, NotificationUserIdProvider>();
builder.Services.AddScoped<INotificationPusher, NotificationPusher>();
// 业务状态变更事件 → SignalR 推送(多端状态同步)
builder.Services.AddScoped<INotificationHandler<BusinessChangedEvent>, BusinessChangedEventHandler>();
// CORS
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
});
// NSwag(OpenAPI 文档 + Swagger UI + 可生成 TS/C# 客户端)
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApiDocument(settings =>
{
settings.Title = "企业 RBAC 管理系统 API";
settings.Version = "v1";
settings.Description = "基于 NSwag 生成的 OpenAPI 文档,可用于生成 TypeScript/C# 客户端";
settings.AddSecurity("Bearer", new NSwag.OpenApiSecurityScheme
{
Type = NSwag.OpenApiSecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT",
Description = "请输入 access token(格式:Bearer {token})"
});
settings.OperationProcessors.Add(new NSwag.Generation.Processors.Security.AspNetCoreOperationSecurityScopeProcessor("Bearer"));
});
var app = builder.Build();
// 初始化数据库(FreeSql CodeFirst 建表 + 种子)
using (var scope = app.Services.CreateScope())
{
var fsql = scope.ServiceProvider.GetRequiredService<IFreeSql>();
var snowflake = scope.ServiceProvider.GetRequiredService<F9WebAiTest.Application.Interfaces.SnowflakeID.ISnowflakeIdGenerator>();
DbSeeder.Seed(fsql, snowflake);
}
// NSwag 中间件(swagger 文档 + UI)
app.UseOpenApi();
app.UseSwaggerUi();
app.UseCors("AllowAll");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapHub<NotificationHub>("/hubs/notification");
app.Run();