feat: 二维码访问功能,统一端点管道增强,端点迁移至 Services 层

- 新增二维码生成端点,自动检测局域网 IP,前端扫一扫即可打开网站
  - 提取 IApiResponse 接口,ServiceRequestBinder 支持强类型请求 DTO 绑定
  - FileStream 端点迁移至 AppEndpoints 统一注册,管道支持 FileStreamResponse 原始文件返回
  - 文件库端点全面使用 MapGet<TService, TRequest> 泛型注册
  - 移除 Avalonia-API/Extensions 中的业务端点文件,统一由 Services 层管理
This commit is contained in:
2026-05-22 11:18:47 +08:00
parent a16c32b25e
commit d84bbb3a18
35 changed files with 888 additions and 284 deletions
@@ -3,7 +3,6 @@ using Avalonia_Common.Core;
using Avalonia_Services.Core;
using Avalonia_Services.Services.AuthService;
using System;
using System.Text.Json;
using System.Threading.Tasks;
namespace Avalonia_PC.Authentication
@@ -14,16 +13,10 @@ namespace Avalonia_PC.Authentication
/// </summary>
public sealed class PcAuthEndpointService(PcGlobalTokenService tokenService) : IPcAuthEndpointService
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true,
};
/// <inheritdoc />
public async Task<object?> AuthorizeAsync(ServiceEndpointContext ctx)
public async Task<IApiResponse> AuthorizeAsync(PcAuthorizeRequest request, ServiceEndpointContext ctx)
{
var request = Deserialize<PcAuthorizeRequest>(ctx.Body);
var token = await tokenService.AuthorizeAsync(request?.AuthorizationCode);
var token = await tokenService.AuthorizeAsync(request.AuthorizationCode);
if (token is null)
{
ctx.StatusCode = 401;
@@ -34,10 +27,9 @@ namespace Avalonia_PC.Authentication
}
/// <inheritdoc />
public async Task<object?> RefreshAsync(ServiceEndpointContext ctx)
public async Task<IApiResponse> RefreshAsync(PcRefreshRequest request, ServiceEndpointContext ctx)
{
var request = Deserialize<PcRefreshRequest>(ctx.Body);
var token = request?.Token ?? ExtractBearerToken(ctx.GetHeader("Authorization"));
var token = request.Token ?? ExtractBearerToken(ctx.GetHeader("Authorization"));
var refreshed = await tokenService.RefreshAsync(token);
if (refreshed is null)
{
@@ -49,25 +41,11 @@ namespace Avalonia_PC.Authentication
}
/// <inheritdoc />
public Task<object?> LogoutAsync(ServiceEndpointContext ctx)
public Task<IApiResponse> LogoutAsync(PcLogoutRequest request, ServiceEndpointContext ctx)
{
var request = Deserialize<PcLogoutRequest>(ctx.Body);
var token = request?.Token ?? ExtractBearerToken(ctx.GetHeader("Authorization"));
var token = request.Token ?? ExtractBearerToken(ctx.GetHeader("Authorization"));
tokenService.Logout(token);
return Task.FromResult<object?>(ResponseHelper.Succeed("退出成功"));
}
/// <summary>
/// 将 JSON 请求体反序列化为指定类型。
/// </summary>
/// <typeparam name="T">目标类型。</typeparam>
/// <param name="body">JSON 请求体字符串,可为空。</param>
/// <returns>反序列化后的对象;若 body 为空则返回默认值。</returns>
private static T? Deserialize<T>(string? body)
{
return string.IsNullOrWhiteSpace(body)
? default
: JsonSerializer.Deserialize<T>(body, JsonOptions);
return Task.FromResult<IApiResponse>(ResponseHelper.Succeed("退出成功"));
}
/// <summary>