first commit

This commit is contained in:
yanpeng200212
2026-10-08 10:43:59 +08:00
commit 8430ba0452
288 changed files with 29595 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
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();