using System.Diagnostics;
using TakeoutSaaS.Shared.Abstractions.Diagnostics;
namespace TakeoutSaaS.Shared.Abstractions.Results;
///
/// 统一的 API 返回结果包装。
///
/// 数据载荷类型。
public sealed record ApiResponse
{
///
/// 是否成功。
///
public bool Success { get; init; }
///
/// 状态/错误码(默认 200)。
///
public int Code { get; init; } = 200;
///
/// 提示信息。
///
public string? Message { get; init; }
///
/// 业务数据。
///
public T? Data { get; init; }
///
/// 错误详情(如字段验证错误)。
///
public object? Errors { get; init; }
///
/// TraceId,便于链路追踪。
///
public string TraceId { get; init; } = string.Empty;
///
/// 时间戳(UTC)。
///
public DateTime Timestamp { get; init; } = DateTime.UtcNow;
///
/// 成功返回。
///
/// 业务数据。
/// 提示信息。
/// 封装后的成功响应。
public static ApiResponse Ok(T data, string? message = "操作成功")
=> Create(true, 200, message, data);
///
/// 无数据的成功返回。
///
/// 提示信息。
/// 封装后的成功响应。
public static ApiResponse Ok(string? message = "操作成功")
=> Create(true, 200, message, default);
///
/// 兼容旧名称:成功结果。
///
/// 业务数据。
/// 提示信息。
/// 封装后的成功响应。
public static ApiResponse SuccessResult(T data, string? message = "操作成功")
=> Ok(data, message);
///
/// 错误返回。
///
/// 错误码。
/// 错误提示。
/// 错误详情。
/// 封装后的失败响应。
public static ApiResponse Error(int code, string message, object? errors = null)
=> Create(false, code, message, default, errors);
///
/// 兼容旧名称:失败结果。
///
/// 错误码。
/// 错误提示。
/// 封装后的失败响应。
public static ApiResponse Failure(int code, string message)
=> Error(code, message);
///
/// 附加错误详情。
///
/// 错误详情。
/// 包含错误详情的新响应。
public ApiResponse WithErrors(object? errors)
=> this with { Errors = errors };
private static ApiResponse Create(bool success, int code, string? message, T? data, object? errors = null)
=> new()
{
Success = success,
Code = code,
Message = message,
Data = data,
Errors = errors,
TraceId = ResolveTraceId(),
Timestamp = DateTime.UtcNow
};
///
/// 解析当前 TraceId。
///
/// 当前有效的 TraceId。
private static string ResolveTraceId()
{
if (!string.IsNullOrWhiteSpace(TraceContext.TraceId))
{
return TraceContext.TraceId;
}
if (!string.IsNullOrWhiteSpace(TraceContext.TraceId))
{
return TraceContext.TraceId;
}
if (Activity.Current?.Id is { } id && !string.IsNullOrWhiteSpace(id))
{
return id;
}
return IdFallbackGenerator.Instance.NextId().ToString();
}
}
///
/// 作为 TraceId 缺失时的本地雪花 ID 备用生成器。
///
internal sealed class IdFallbackGenerator
{
///
/// 延迟初始化的单例实例承载。
///
private static readonly Lazy Lazy = new(() => new IdFallbackGenerator());
///
/// 获取备用雪花生成器单例。
///
public static IdFallbackGenerator Instance => Lazy.Value;
private readonly object _sync = new();
private long _lastTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
private long _sequence;
private IdFallbackGenerator()
{
}
///
/// 生成雪花风格的本地备用 ID。
///
/// 本地生成的雪花 ID。
public long NextId()
{
lock (_sync)
{
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
if (timestamp == _lastTimestamp)
{
_sequence = (_sequence + 1) & 4095;
if (_sequence == 0)
{
timestamp = WaitNextMillis(_lastTimestamp);
}
}
else
{
_sequence = 0;
}
_lastTimestamp = timestamp;
return ((timestamp - 1577836800000L) << 22) | _sequence;
}
}
///
/// 等待到下一个毫秒以避免序列冲突。
///
/// 上一毫秒的时间戳。
/// 下一个时间戳(毫秒)。
private static long WaitNextMillis(long lastTimestamp)
{
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
while (timestamp <= lastTimestamp)
{
Thread.SpinWait(100);
timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
return timestamp;
}
}