2025-12-25 11:20:56 +08:00
|
|
|
|
using FreeSql;
|
2025-12-25 13:27:23 +08:00
|
|
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
2026-01-10 10:06:39 +08:00
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
|
using Microsoft.AspNetCore.Mvc.Authorization;
|
2025-12-25 13:27:23 +08:00
|
|
|
|
using Microsoft.IdentityModel.Tokens;
|
2025-12-25 11:20:56 +08:00
|
|
|
|
using Scalar.AspNetCore;
|
|
|
|
|
|
using Serilog;
|
|
|
|
|
|
using Service.AppSettingModel;
|
2026-02-16 21:55:38 +08:00
|
|
|
|
using Service.IconSearch;
|
2025-12-29 15:20:32 +08:00
|
|
|
|
using WebApi;
|
2026-01-22 19:06:58 +08:00
|
|
|
|
using WebApi.Middleware;
|
2026-02-10 17:49:19 +08:00
|
|
|
|
using WebApi.Filters;
|
2025-12-25 11:20:56 +08:00
|
|
|
|
using Yitter.IdGenerator;
|
2026-02-10 17:49:19 +08:00
|
|
|
|
using Application.Extensions;
|
2025-12-25 11:20:56 +08:00
|
|
|
|
|
|
|
|
|
|
// 初始化雪花算法ID生成器
|
|
|
|
|
|
var options = new IdGeneratorOptions(1); // WorkerId 为 1,可根据实际部署情况调整
|
|
|
|
|
|
YitIdHelper.SetIdGenerator(options);
|
|
|
|
|
|
|
|
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
|
|
|
|
|
|
|
|
// 配置 Serilog
|
|
|
|
|
|
builder.Host.UseSerilog((context, loggerConfig) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
loggerConfig.ReadFrom.Configuration(context.Configuration);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Add services to the container.
|
2026-01-18 22:25:59 +08:00
|
|
|
|
builder.Services.AddControllers(mvcOptions =>
|
2026-01-10 10:06:39 +08:00
|
|
|
|
{
|
|
|
|
|
|
var policy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
|
|
|
|
|
|
.RequireAuthenticatedUser()
|
|
|
|
|
|
.Build();
|
|
|
|
|
|
|
2026-01-18 22:25:59 +08:00
|
|
|
|
mvcOptions.Filters.Add(new AuthorizeFilter(policy));
|
2026-02-10 17:49:19 +08:00
|
|
|
|
mvcOptions.Filters.Add<GlobalExceptionFilter>(); // 添加全局异常过滤器
|
2026-01-10 10:06:39 +08:00
|
|
|
|
});
|
2025-12-25 11:20:56 +08:00
|
|
|
|
builder.Services.AddOpenApi();
|
|
|
|
|
|
builder.Services.AddHttpClient();
|
|
|
|
|
|
|
|
|
|
|
|
// 配置 CORS
|
2026-01-18 22:25:59 +08:00
|
|
|
|
builder.Services.AddCors(corsOptions =>
|
2025-12-25 11:20:56 +08:00
|
|
|
|
{
|
2026-01-18 22:25:59 +08:00
|
|
|
|
corsOptions.AddDefaultPolicy(policy =>
|
2025-12-25 11:20:56 +08:00
|
|
|
|
{
|
|
|
|
|
|
policy.WithOrigins("http://localhost:5173")
|
|
|
|
|
|
.AllowAnyHeader()
|
|
|
|
|
|
.AllowAnyMethod();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 绑定配置
|
|
|
|
|
|
builder.Services.Configure<EmailSettings>(builder.Configuration.GetSection("EmailSettings"));
|
2026-01-18 22:04:56 +08:00
|
|
|
|
builder.Services.Configure<AiSettings>(builder.Configuration.GetSection("OpenAI"));
|
2025-12-25 13:27:23 +08:00
|
|
|
|
builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("JwtSettings"));
|
|
|
|
|
|
builder.Services.Configure<AuthSettings>(builder.Configuration.GetSection("AuthSettings"));
|
2026-02-16 21:55:38 +08:00
|
|
|
|
builder.Services.Configure<IconifySettings>(builder.Configuration.GetSection("IconifySettings"));
|
|
|
|
|
|
builder.Services.Configure<IconPromptSettings>(builder.Configuration.GetSection("IconPromptSettings"));
|
|
|
|
|
|
builder.Services.Configure<SearchKeywordSettings>(builder.Configuration.GetSection("SearchKeywordSettings"));
|
|
|
|
|
|
|
2025-12-25 13:27:23 +08:00
|
|
|
|
|
|
|
|
|
|
// 配置JWT认证
|
|
|
|
|
|
var jwtSettings = builder.Configuration.GetSection("JwtSettings");
|
|
|
|
|
|
var secretKey = jwtSettings["SecretKey"]!;
|
|
|
|
|
|
var key = Encoding.UTF8.GetBytes(secretKey);
|
|
|
|
|
|
|
2026-01-18 22:25:59 +08:00
|
|
|
|
builder.Services.AddAuthentication(authenticationOptions =>
|
2025-12-25 13:27:23 +08:00
|
|
|
|
{
|
2026-01-18 22:25:59 +08:00
|
|
|
|
authenticationOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
|
|
|
|
authenticationOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
2025-12-25 13:27:23 +08:00
|
|
|
|
})
|
2026-01-18 22:25:59 +08:00
|
|
|
|
.AddJwtBearer(jwtBearerOptions =>
|
2025-12-25 13:27:23 +08:00
|
|
|
|
{
|
2026-01-18 22:25:59 +08:00
|
|
|
|
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
|
2025-12-25 13:27:23 +08:00
|
|
|
|
{
|
|
|
|
|
|
ValidateIssuer = true,
|
|
|
|
|
|
ValidateAudience = true,
|
|
|
|
|
|
ValidateLifetime = true,
|
|
|
|
|
|
ValidateIssuerSigningKey = true,
|
|
|
|
|
|
ValidIssuer = jwtSettings["Issuer"],
|
|
|
|
|
|
ValidAudience = jwtSettings["Audience"],
|
|
|
|
|
|
IssuerSigningKey = new SymmetricSecurityKey(key),
|
|
|
|
|
|
ClockSkew = TimeSpan.Zero
|
|
|
|
|
|
};
|
2026-01-18 22:25:59 +08:00
|
|
|
|
jwtBearerOptions.Events = new JwtBearerEvents
|
2026-01-10 10:06:39 +08:00
|
|
|
|
{
|
|
|
|
|
|
OnChallenge = async context =>
|
|
|
|
|
|
{
|
|
|
|
|
|
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
|
await context.Response.WriteAsJsonAsync(BaseResponse.Fail("未登录"));
|
|
|
|
|
|
},
|
|
|
|
|
|
OnForbidden = async context =>
|
|
|
|
|
|
{
|
|
|
|
|
|
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
|
await context.Response.WriteAsJsonAsync(BaseResponse.Fail("权限不足"));
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2025-12-25 13:27:23 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
builder.Services.AddAuthorization();
|
2025-12-25 11:20:56 +08:00
|
|
|
|
|
|
|
|
|
|
// 配置 FreeSql + SQLite
|
|
|
|
|
|
var dbPath = Path.Combine(AppContext.BaseDirectory, "database");
|
|
|
|
|
|
if (!Directory.Exists(dbPath))
|
|
|
|
|
|
{
|
|
|
|
|
|
Directory.CreateDirectory(dbPath);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用绝对路径作为数据库文件路径
|
|
|
|
|
|
var dbFilePath = Path.Combine(dbPath, "EmailBill.db");
|
|
|
|
|
|
var connectionString = $"Data Source={dbFilePath}";
|
|
|
|
|
|
Log.Information("数据库路径: {DbPath}", dbFilePath);
|
|
|
|
|
|
|
|
|
|
|
|
var fsql = new FreeSqlBuilder()
|
|
|
|
|
|
.UseConnectionString(DataType.Sqlite, connectionString)
|
|
|
|
|
|
.UseAutoSyncStructure(true)
|
|
|
|
|
|
.UseLazyLoading(true)
|
|
|
|
|
|
.UseMonitorCommand(
|
2026-01-30 10:41:19 +08:00
|
|
|
|
cmd =>
|
2025-12-25 11:20:56 +08:00
|
|
|
|
{
|
2026-01-12 16:58:51 +08:00
|
|
|
|
Log.Verbose("执行SQL: {Sql}", cmd.CommandText);
|
2025-12-25 11:20:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
)
|
|
|
|
|
|
.Build();
|
|
|
|
|
|
|
2026-01-12 22:29:39 +08:00
|
|
|
|
fsql.UseJsonMap();
|
|
|
|
|
|
|
2025-12-25 11:20:56 +08:00
|
|
|
|
builder.Services.AddSingleton(fsql);
|
|
|
|
|
|
|
|
|
|
|
|
// 自动扫描注册服务和仓储
|
|
|
|
|
|
builder.Services.AddServices();
|
|
|
|
|
|
|
2026-02-10 17:49:19 +08:00
|
|
|
|
// 注册Application层服务
|
|
|
|
|
|
builder.Services.AddApplicationServices();
|
|
|
|
|
|
|
2025-12-29 15:20:32 +08:00
|
|
|
|
// 配置 Quartz.NET 定时任务
|
|
|
|
|
|
builder.AddScheduler();
|
|
|
|
|
|
|
2025-12-25 11:20:56 +08:00
|
|
|
|
var app = builder.Build();
|
|
|
|
|
|
|
|
|
|
|
|
// Configure the HTTP request pipeline.
|
|
|
|
|
|
if (app.Environment.IsDevelopment())
|
|
|
|
|
|
{
|
|
|
|
|
|
app.MapOpenApi();
|
|
|
|
|
|
app.MapScalarApiReference();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 启用静态文件服务
|
|
|
|
|
|
app.UseDefaultFiles();
|
|
|
|
|
|
app.UseStaticFiles();
|
|
|
|
|
|
|
|
|
|
|
|
// 启用 CORS
|
|
|
|
|
|
app.UseCors();
|
|
|
|
|
|
|
2026-01-22 19:06:58 +08:00
|
|
|
|
// 启用请求ID跟踪
|
|
|
|
|
|
app.UseRequestId();
|
|
|
|
|
|
|
2025-12-25 13:27:23 +08:00
|
|
|
|
// 启用认证和授权
|
|
|
|
|
|
app.UseAuthentication();
|
|
|
|
|
|
app.UseAuthorization();
|
|
|
|
|
|
|
2025-12-25 11:20:56 +08:00
|
|
|
|
app.MapControllers();
|
|
|
|
|
|
|
|
|
|
|
|
// 添加 SPA 回退路由(用于前端路由)
|
|
|
|
|
|
app.MapFallbackToFile("index.html");
|
|
|
|
|
|
|
|
|
|
|
|
app.Run();
|