2026-05-11 14:35:34 +08:00
|
|
|
|
using Avalonia_Common.Core;
|
2026-05-15 15:26:46 +08:00
|
|
|
|
using Avalonia_EFCore.Database;
|
|
|
|
|
|
using Avalonia_EFCore.Models;
|
2026-05-11 14:35:34 +08:00
|
|
|
|
using Avalonia_Services.Core;
|
|
|
|
|
|
using Avalonia_Services.Services;
|
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Avalonia_Services.Endpoints
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 统一端点配置 —— 所有业务端点在此定义一次。
|
|
|
|
|
|
/// 这是 Avalonia-API 和 Avalonia-PC 的唯一入口。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static class AppEndpoints
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 配置所有业务端点。调用方传入 builder,按需叠加鉴权、过滤器等。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="builder">端点构建器</param>
|
|
|
|
|
|
/// <param name="includeDetails">是否在错误响应中包含异常详情(开发环境 true)</param>
|
|
|
|
|
|
public static ServiceEndpointBuilder Configure(ServiceEndpointBuilder builder, bool includeDetails = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
// ---- 全局异常拦截(自动捕获所有端点中未处理的异常) ----
|
|
|
|
|
|
builder.Endpoints.AddGlobalFilter(new GlobalExceptionFilter(includeDetails));
|
|
|
|
|
|
|
|
|
|
|
|
builder.ConfigureEndpoints(endpoints =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// ---- 全局日志过滤器(记录每个请求) ----
|
|
|
|
|
|
endpoints.AddGlobalFilter(async (ctx, next) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
Serilog.Log.Debug("→ {Method} {Path}", ctx.Method, ctx.Path);
|
|
|
|
|
|
await next(ctx);
|
|
|
|
|
|
Serilog.Log.Debug("← {Method} {Path} | {StatusCode}", ctx.Method, ctx.Path, ctx.StatusCode);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// ---- 业务端点注册 ----
|
|
|
|
|
|
// 天气预报(从数据库读取)
|
|
|
|
|
|
endpoints.MapGet("api/wData", GetWeatherForecastsAsync)
|
2026-05-15 17:35:07 +08:00
|
|
|
|
.WithOpenApi("Weather", "获取天气预报信息。")
|
2026-05-11 14:35:34 +08:00
|
|
|
|
.WithName("GetWeatherForecast");
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户(演示从数据库查询)
|
|
|
|
|
|
endpoints.MapGet("api/getUser", GetUserFromDatabaseAsync)
|
|
|
|
|
|
.WithName("GetUser");
|
|
|
|
|
|
|
|
|
|
|
|
// 处理数据(POST — 演示参数处理)
|
|
|
|
|
|
endpoints.MapPost("api/processData", ProcessDataAsync)
|
|
|
|
|
|
.WithName("ProcessData");
|
|
|
|
|
|
|
|
|
|
|
|
// ---- 需要鉴权的端点示例 ----
|
|
|
|
|
|
// endpoints.MapGet("api/admin/dashboard", AdminDashboardAsync)
|
|
|
|
|
|
// .WithName("AdminDashboard")
|
|
|
|
|
|
// .RequireAuthorization = true
|
|
|
|
|
|
// .Policy = "AdminOnly";
|
|
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return builder;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#region 业务处理方法
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 从数据库查询天气预报(优先数据库,回退到内存生成)。
|
|
|
|
|
|
/// </summary>
|
2026-05-22 11:42:38 +08:00
|
|
|
|
private static async Task<IApiResponse> GetWeatherForecastsAsync(ServiceEndpointContext ctx)
|
2026-05-11 14:35:34 +08:00
|
|
|
|
{
|
|
|
|
|
|
var sp = ctx.Items["ServiceProvider"] as IServiceProvider;
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试从数据库读取
|
|
|
|
|
|
if (sp?.GetService(typeof(AppDataContext)) is AppDataContext db)
|
|
|
|
|
|
{
|
|
|
|
|
|
var dbForecasts = await db.WeatherForecasts
|
|
|
|
|
|
.OrderByDescending(f => f.Date)
|
|
|
|
|
|
.Take(5)
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (dbForecasts.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResponseHelper.Ok(dbForecasts, "获取天气预报成功(来自数据库)");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 回退:内存生成(数据库为空时)
|
|
|
|
|
|
var service = sp?.GetService(typeof(WeatherForecastService)) as WeatherForecastService
|
|
|
|
|
|
?? new WeatherForecastService();
|
|
|
|
|
|
|
|
|
|
|
|
var forecasts = service.GetWeatherForecasts();
|
|
|
|
|
|
return ResponseHelper.Ok(forecasts, "获取天气预报成功(内存生成)");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-18 11:35:13 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 从数据库获取用户信息(演示数据库查询),若无数据则返回演示用户。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="ctx">服务端点上下文。</param>
|
|
|
|
|
|
/// <returns>用户信息。</returns>
|
2026-05-22 11:42:38 +08:00
|
|
|
|
private static async Task<IApiResponse> GetUserFromDatabaseAsync(ServiceEndpointContext ctx)
|
2026-05-11 14:35:34 +08:00
|
|
|
|
{
|
|
|
|
|
|
var sp = ctx.Items["ServiceProvider"] as IServiceProvider;
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试从数据库读取用户
|
|
|
|
|
|
if (sp?.GetService(typeof(AppDataContext)) is AppDataContext db)
|
|
|
|
|
|
{
|
|
|
|
|
|
var users = await db.Set<UserEntity>().Take(1).ToListAsync();
|
|
|
|
|
|
if (users.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResponseHelper.Ok(users[0], "获取用户成功(来自数据库)");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 回退:演示数据
|
|
|
|
|
|
await Task.Delay(100);
|
|
|
|
|
|
var user = new { id = 1, name = "张三", email = "zhangsan@example.com" };
|
|
|
|
|
|
return ResponseHelper.Ok(user);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-18 11:35:13 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理前端发送的数据(POST 演示),将数据存入数据库或转为大写返回。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="ctx">服务端点上下文。</param>
|
|
|
|
|
|
/// <returns>处理结果。</returns>
|
2026-05-22 11:42:38 +08:00
|
|
|
|
private static async Task<IApiResponse> ProcessDataAsync(ServiceEndpointContext ctx)
|
2026-05-11 14:35:34 +08:00
|
|
|
|
{
|
|
|
|
|
|
var sp = ctx.Items["ServiceProvider"] as IServiceProvider;
|
|
|
|
|
|
|
|
|
|
|
|
// 演示:将收到的数据存入数据库
|
|
|
|
|
|
var input = ctx.Body ?? string.Empty;
|
|
|
|
|
|
if (sp?.GetService(typeof(AppDataContext)) is AppDataContext db && !string.IsNullOrWhiteSpace(input))
|
|
|
|
|
|
{
|
|
|
|
|
|
var forecast = new WeatherForecastEntity
|
|
|
|
|
|
{
|
|
|
|
|
|
Date = DateOnly.FromDateTime(DateTime.Now),
|
|
|
|
|
|
TemperatureC = 20,
|
|
|
|
|
|
Summary = input,
|
|
|
|
|
|
};
|
|
|
|
|
|
db.WeatherForecasts.Add(forecast);
|
|
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
|
|
return ResponseHelper.Ok(forecast, "数据已存入数据库");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await Task.Delay(200);
|
|
|
|
|
|
return ResponseHelper.Ok(new { input, processed = input.ToUpperInvariant() });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|