218 lines
7.0 KiB
C#
218 lines
7.0 KiB
C#
using System.Text.Json.Serialization;
|
||
|
||
namespace FileShare_Common.Core
|
||
{
|
||
/// <summary>
|
||
/// 统一端点响应契约。
|
||
/// </summary>
|
||
public interface IApiResponse
|
||
{
|
||
/// <summary>是否成功。</summary>
|
||
bool Success { get; }
|
||
|
||
/// <summary>业务状态码。</summary>
|
||
int Code { get; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 统一 API 返回格式。
|
||
/// 所有接口的返回都包装为此格式,确保前端收到一致的数据结构。
|
||
/// </summary>
|
||
/// <typeparam name="T">业务数据类型</typeparam>
|
||
public class ApiResponse<T> : IApiResponse
|
||
{
|
||
/// <summary>是否成功</summary>
|
||
[JsonPropertyName("success")]
|
||
public bool Success { get; set; }
|
||
|
||
/// <summary>HTTP 状态码</summary>
|
||
[JsonPropertyName("code")]
|
||
public int Code { get; set; }
|
||
|
||
/// <summary>消息(成功时可为 null,失败时包含错误描述)</summary>
|
||
[JsonPropertyName("message")]
|
||
public string? Message { get; set; }
|
||
|
||
/// <summary>业务数据</summary>
|
||
[JsonPropertyName("data")]
|
||
public T? Data { get; set; }
|
||
|
||
/// <summary>时间戳</summary>
|
||
[JsonPropertyName("timestamp")]
|
||
public DateTime Timestamp { get; set; } = DateTime.Now;
|
||
|
||
/// <summary>请求追踪 ID(用于排查问题)</summary>
|
||
[JsonPropertyName("traceId")]
|
||
public string? TraceId { get; set; }
|
||
|
||
// ---- 快捷工厂方法 ----
|
||
|
||
/// <summary>成功返回(有数据)</summary>
|
||
public static ApiResponse<T> Ok(T data, string? message = null)
|
||
{
|
||
return new ApiResponse<T>
|
||
{
|
||
Success = true,
|
||
Code = 200,
|
||
Message = message,
|
||
Data = data,
|
||
};
|
||
}
|
||
|
||
/// <summary>失败返回</summary>
|
||
public static ApiResponse<T> Fail(int code, string message, T? data = default)
|
||
{
|
||
return new ApiResponse<T>
|
||
{
|
||
Success = false,
|
||
Code = code,
|
||
Message = message,
|
||
Data = data,
|
||
};
|
||
}
|
||
|
||
/// <summary>400 参数错误</summary>
|
||
public static ApiResponse<T> BadRequest(string message = "参数错误")
|
||
=> Fail(400, message);
|
||
|
||
/// <summary>401 未授权</summary>
|
||
public static ApiResponse<T> Unauthorized(string message = "未授权")
|
||
=> Fail(401, message);
|
||
|
||
/// <summary>403 无权限</summary>
|
||
public static ApiResponse<T> Forbidden(string message = "无权限")
|
||
=> Fail(403, message);
|
||
|
||
/// <summary>404 未找到</summary>
|
||
public static ApiResponse<T> NotFound(string message = "资源不存在")
|
||
=> Fail(404, message);
|
||
|
||
/// <summary>500 服务器内部错误</summary>
|
||
public static ApiResponse<T> ServerError(string message = "服务器内部错误")
|
||
=> Fail(500, message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 无数据的统一返回格式(object? 版本)。
|
||
/// </summary>
|
||
public class ApiResponse : ApiResponse<object?>
|
||
{
|
||
/// <summary>成功返回(无数据)</summary>
|
||
public static ApiResponse Succeed(string? message = null)
|
||
{
|
||
return new ApiResponse
|
||
{
|
||
Success = true,
|
||
Code = 200,
|
||
Message = message,
|
||
Data = null,
|
||
};
|
||
}
|
||
|
||
/// <summary>失败返回</summary>
|
||
public static ApiResponse Failure(int code, string message)
|
||
{
|
||
return new ApiResponse
|
||
{
|
||
Success = false,
|
||
Code = code,
|
||
Message = message,
|
||
Data = null,
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分页返回格式
|
||
/// </summary>
|
||
public class PagedResponse<T> : IApiResponse
|
||
{
|
||
/// <summary>
|
||
/// 获取或设置操作是否成功。
|
||
/// </summary>
|
||
[JsonPropertyName("success")]
|
||
public bool Success { get; set; } = true;
|
||
|
||
/// <summary>
|
||
/// 获取或设置业务状态码,默认 200。
|
||
/// </summary>
|
||
[JsonPropertyName("code")]
|
||
public int Code { get; set; } = 200;
|
||
|
||
/// <summary>
|
||
/// 获取或设置分页数据项列表。
|
||
/// </summary>
|
||
[JsonPropertyName("items")]
|
||
public List<T> Items { get; set; } = new();
|
||
|
||
/// <summary>
|
||
/// 获取或设置数据总条数。
|
||
/// </summary>
|
||
[JsonPropertyName("total")]
|
||
public int Total { get; set; }
|
||
|
||
/// <summary>
|
||
/// 获取或设置当前页码,从 1 开始。
|
||
/// </summary>
|
||
[JsonPropertyName("page")]
|
||
public int Page { get; set; } = 1;
|
||
|
||
/// <summary>
|
||
/// 获取或设置每页条数,默认 20。
|
||
/// </summary>
|
||
[JsonPropertyName("pageSize")]
|
||
public int PageSize { get; set; } = 20;
|
||
|
||
/// <summary>
|
||
/// 获取总页数(根据 Total 和 PageSize 自动计算)。
|
||
/// </summary>
|
||
[JsonPropertyName("totalPages")]
|
||
public int TotalPages => PageSize > 0 ? (int)Math.Ceiling((double)Total / PageSize) : 0;
|
||
|
||
/// <summary>
|
||
/// 从数据列表和分页参数创建分页响应。
|
||
/// </summary>
|
||
/// <param name="items">当前页数据项。</param>
|
||
/// <param name="total">数据总条数。</param>
|
||
/// <param name="page">当前页码。</param>
|
||
/// <param name="pageSize">每页条数。</param>
|
||
/// <returns>分页响应实例。</returns>
|
||
public static PagedResponse<T> From(List<T> items, int total, int page, int pageSize)
|
||
{
|
||
return new PagedResponse<T>
|
||
{
|
||
Items = items,
|
||
Total = total,
|
||
Page = page,
|
||
PageSize = pageSize,
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 端点返回辅助方法 —— 在 AppEndpoints 中快捷构建统一响应。
|
||
/// </summary>
|
||
public static class ResponseHelper
|
||
{
|
||
/// <summary>成功返回</summary>
|
||
public static ApiResponse<T> Ok<T>(T data, string? message = null)
|
||
=> ApiResponse<T>.Ok(data, message);
|
||
|
||
/// <summary>成功返回(无数据)</summary>
|
||
public static ApiResponse Succeed(string? message = null)
|
||
=> ApiResponse.Succeed(message);
|
||
|
||
/// <summary>失败返回</summary>
|
||
public static ApiResponse<T> Fail<T>(int code, string message, T? data = default)
|
||
=> ApiResponse<T>.Fail(code, message, data);
|
||
|
||
/// <summary>失败返回(无数据)</summary>
|
||
public static ApiResponse Failure(int code, string message)
|
||
=> ApiResponse.Failure(code, message);
|
||
|
||
/// <summary>分页返回</summary>
|
||
public static PagedResponse<T> Paged<T>(List<T> items, int total, int page, int pageSize)
|
||
=> PagedResponse<T>.From(items, total, page, pageSize);
|
||
}
|
||
}
|