docs: 补全全部缺失的 XML 文档注释(中文)

- 为全部 5 个项目(Avalonia-API、Avalonia-Common、Avalonia-EFCore、
  Avalonia-PC、Avalonia-Services)中缺失注释的类、方法、属性、字段、
  接口成员等补全中文 XML 文档注释
- 共修改约 37 个文件,补全约 220+ 处注释
- 修复 ServiceEndpointCollection.cs 中 MapDelete<TService> 语法错误
- 修复 PcAuthService.cs 中 const prefix 位置错乱导致编译失败的问题
- 扫描结果:缺失项 0
- 构建结果:4/4 项目编译通过
This commit is contained in:
2026-05-18 11:35:13 +08:00
parent 446502b0e9
commit fc6f9f6bc3
43 changed files with 884 additions and 233 deletions
@@ -8,6 +8,10 @@ using System.Text.Json;
namespace Avalonia_API.Authentication
{
/// <summary>
/// API 鉴权端点服务,实现 <see cref="IApiAuthEndpointService"/>
/// 处理登录、刷新 Token 和登出操作,使用 JWT 与 Refresh Token 机制。
/// </summary>
public sealed class ApiAuthEndpointService(
AppDataContext db,
JwtTokenService jwtTokenService,
@@ -18,6 +22,12 @@ namespace Avalonia_API.Authentication
PropertyNameCaseInsensitive = true,
};
/// <summary>
/// 处理用户登录请求。根据账号(邮箱或用户名)查找或创建用户,
/// 生成 JWT Access Token 和 Refresh Token 并返回。
/// </summary>
/// <param name="ctx">服务端点上下文,包含请求体、请求头等信息。</param>
/// <returns>包含 AccessToken、RefreshToken 及过期时间的认证响应。</returns>
public async Task<object?> LoginAsync(ServiceEndpointContext ctx)
{
var request = Deserialize<ApiLoginRequest>(ctx.Body);
@@ -56,6 +66,12 @@ namespace Avalonia_API.Authentication
roles), "登录成功");
}
/// <summary>
/// 使用 Refresh Token 轮换新的 Access Token 和 Refresh Token。
/// 旧的 Refresh Token 会被撤销并替换。
/// </summary>
/// <param name="ctx">服务端点上下文,包含请求体中的 RefreshToken。</param>
/// <returns>新的 Token 对;若 Refresh Token 无效则返回 401 错误。</returns>
public async Task<object?> RefreshAsync(ServiceEndpointContext ctx)
{
var request = Deserialize<ApiRefreshTokenRequest>(ctx.Body);
@@ -88,6 +104,11 @@ namespace Avalonia_API.Authentication
roles), "刷新成功");
}
/// <summary>
/// 处理用户登出请求,撤销指定的 Refresh Token。
/// </summary>
/// <param name="ctx">服务端点上下文,包含请求体中的 RefreshToken。</param>
/// <returns>登出成功的响应。</returns>
public async Task<object?> LogoutAsync(ServiceEndpointContext ctx)
{
var request = Deserialize<ApiLogoutRequest>(ctx.Body);
@@ -95,6 +116,12 @@ namespace Avalonia_API.Authentication
return 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)
@@ -102,6 +129,11 @@ namespace Avalonia_API.Authentication
: JsonSerializer.Deserialize<T>(body, JsonOptions);
}
/// <summary>
/// 从上下文的 Items 中提取 ASP.NET Core HttpContext,并获取客户端远程 IP 地址。
/// </summary>
/// <param name="ctx">服务端点上下文。</param>
/// <returns>客户端 IP 地址字符串;若无法获取则返回 null。</returns>
private static string? GetRemoteIpAddress(ServiceEndpointContext ctx)
{
return ctx.Items.TryGetValue("HttpContext", out var value) && value is HttpContext httpContext
@@ -109,6 +141,11 @@ namespace Avalonia_API.Authentication
: null;
}
/// <summary>
/// 规范化角色数组:去空白、去重(忽略大小写),为空时默认返回 Admin 角色。
/// </summary>
/// <param name="roles">原始角色数组,可为 null。</param>
/// <returns>规范化后的角色数组。</returns>
private static string[] NormalizeRoles(string[]? roles)
{
var normalized = roles?
+18
View File
@@ -1,15 +1,33 @@
namespace Avalonia_API.Authentication
{
/// <summary>
/// JWT 鉴权配置选项,从 appsettings.json 的 Jwt 节绑定。
/// </summary>
public sealed class JwtOptions
{
/// <summary>
/// 获取或设置 Token 签发者。
/// </summary>
public string Issuer { get; set; } = "Avalonia-API";
/// <summary>
/// 获取或设置 Token 受众。
/// </summary>
public string Audience { get; set; } = "Avalonia-Client";
/// <summary>
/// 获取或设置签名密钥(至少 32 字节)。
/// </summary>
public string SigningKey { get; set; } = "change-this-development-signing-key-at-least-32-bytes";
/// <summary>
/// 获取或设置 Access Token 有效期(分钟),默认 60 分钟。
/// </summary>
public int AccessTokenMinutes { get; set; } = 60;
/// <summary>
/// 获取或设置 Refresh Token 有效期(天),默认 30 天。
/// </summary>
public int RefreshTokenDays { get; set; } = 30;
}
}
@@ -7,10 +7,22 @@ using System.Text;
namespace Avalonia_API.Authentication
{
/// <summary>
/// JWT Token 服务,负责创建包含用户声明和角色的 Access Token。
/// </summary>
public sealed class JwtTokenService(IOptions<JwtOptions> options)
{
/// <summary>
/// JWT 配置选项。
/// </summary>
private readonly JwtOptions _options = options.Value;
/// <summary>
/// 创建包含用户声明和角色的 JWT Access Token。
/// </summary>
/// <param name="user">用户实体。</param>
/// <param name="roles">角色集合。</param>
/// <returns>包含 Token 字符串和过期时间的元组。</returns>
public (string Token, DateTime ExpiresAt) CreateAccessToken(UserEntity user, IReadOnlyCollection<string> roles)
{
var expiresAt = DateTime.UtcNow.AddMinutes(_options.AccessTokenMinutes);
@@ -7,10 +7,25 @@ using System.Text;
namespace Avalonia_API.Authentication
{
/// <summary>
/// Refresh Token 服务,负责创建、查找、撤销和轮换 Refresh Token
/// Token 原文经 SHA256 哈希后存入数据库以保证安全性。
/// </summary>
public sealed class RefreshTokenService(AppDataContext db, IOptions<JwtOptions> options)
{
/// <summary>
/// JWT 配置选项。
/// </summary>
private readonly JwtOptions _options = options.Value;
/// <summary>
/// 创建一个新的 Refresh Token,生成随机 Token 原文并存储其哈希到数据库。
/// </summary>
/// <param name="userId">关联的用户 ID。</param>
/// <param name="device">创建设备标识(如 User-Agent)。</param>
/// <param name="ipAddress">客户端 IP 地址。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>包含 Token 原文和实体记录的元组。</returns>
public async Task<(string Token, ApiRefreshTokenEntity Entity)> CreateAsync(
int userId,
string? device,
@@ -32,6 +47,13 @@ namespace Avalonia_API.Authentication
return (token, entity);
}
/// <summary>
/// 查找有效的 Refresh Token 实体。Token 原文会被哈希后查询数据库,
/// 仅返回未过期且未被撤销的 Token。
/// </summary>
/// <param name="token">Refresh Token 原文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>有效的 Token 实体;若无效或不存在则返回 null。</returns>
public async Task<ApiRefreshTokenEntity?> FindActiveAsync(string? token, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(token))
@@ -44,6 +66,11 @@ namespace Avalonia_API.Authentication
return entity?.IsActive == true ? entity : null;
}
/// <summary>
/// 撤销指定的 Refresh Token,将其 RevokedAt 设为当前时间。
/// </summary>
/// <param name="token">要撤销的 Refresh Token 原文。</param>
/// <param name="cancellationToken">取消令牌。</param>
public async Task RevokeAsync(string? token, CancellationToken cancellationToken = default)
{
var entity = await FindActiveAsync(token, cancellationToken);
@@ -56,6 +83,14 @@ namespace Avalonia_API.Authentication
await db.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// 轮换 Refresh Token:撤销旧的并创建新的,将新 Token 的哈希关联到旧记录。
/// </summary>
/// <param name="token">旧的 Refresh Token 原文。</param>
/// <param name="device">当前设备标识。</param>
/// <param name="ipAddress">当前客户端 IP 地址。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>新的 Token 对;若旧 Token 无效则返回 null。</returns>
public async Task<(string Token, ApiRefreshTokenEntity Entity)?> RotateAsync(
string? token,
string? device,
@@ -75,6 +110,11 @@ namespace Avalonia_API.Authentication
return next;
}
/// <summary>
/// 对 Token 原文进行 SHA256 哈希,返回十六进制字符串。
/// </summary>
/// <param name="token">Token 原文。</param>
/// <returns>SHA256 哈希后的十六进制字符串。</returns>
private static string HashToken(string token)
{
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(token));