157 lines
5.1 KiB
C#
157 lines
5.1 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
||
using DramaLing.Api.Models.DTOs;
|
||
using DramaLing.Api.Services;
|
||
using System.Security.Claims;
|
||
|
||
namespace DramaLing.Api.Controllers;
|
||
|
||
[ApiController]
|
||
public abstract class BaseController : ControllerBase
|
||
{
|
||
protected readonly ILogger _logger;
|
||
protected readonly IAuthService? _authService;
|
||
|
||
protected BaseController(ILogger logger, IAuthService? authService = null)
|
||
{
|
||
_logger = logger;
|
||
_authService = authService;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 統一的成功響應格式
|
||
/// </summary>
|
||
protected IActionResult SuccessResponse<T>(T data, string? message = null)
|
||
{
|
||
return Ok(new ApiResponse<T>
|
||
{
|
||
Success = true,
|
||
Data = data,
|
||
Message = message,
|
||
Timestamp = DateTime.UtcNow
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 統一的錯誤響應格式
|
||
/// </summary>
|
||
protected IActionResult ErrorResponse(string code, string message, object? details = null, int statusCode = 500)
|
||
{
|
||
var response = new ApiErrorResponse
|
||
{
|
||
Success = false,
|
||
Error = new ApiError
|
||
{
|
||
Code = code,
|
||
Message = message,
|
||
Details = details,
|
||
Suggestions = GetSuggestionsForError(code)
|
||
},
|
||
RequestId = Guid.NewGuid().ToString(),
|
||
Timestamp = DateTime.UtcNow
|
||
};
|
||
|
||
return StatusCode(statusCode, response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 獲取當前用戶ID(統一處理認證)
|
||
/// </summary>
|
||
protected async Task<Guid> GetCurrentUserIdAsync()
|
||
{
|
||
if (_authService != null)
|
||
{
|
||
// 使用AuthService進行JWT解析(適用於已實現認證的Controller)
|
||
var userId = await _authService.GetUserIdFromTokenAsync(Request.Headers.Authorization);
|
||
if (userId.HasValue)
|
||
return userId.Value;
|
||
}
|
||
|
||
// Fallback: 從Claims直接解析
|
||
var userIdString = User.FindFirst(ClaimTypes.NameIdentifier)?.Value ??
|
||
User.FindFirst("sub")?.Value;
|
||
|
||
if (Guid.TryParse(userIdString, out var parsedUserId))
|
||
return parsedUserId;
|
||
|
||
// 開發階段:使用固定測試用戶ID
|
||
if (IsTestEnvironment())
|
||
{
|
||
return Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||
}
|
||
|
||
throw new UnauthorizedAccessException("Invalid or missing user authentication");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 檢查是否為測試環境
|
||
/// </summary>
|
||
protected bool IsTestEnvironment()
|
||
{
|
||
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
|
||
return environment == "Development" || environment == "Testing";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 統一的模型驗證錯誤處理
|
||
/// </summary>
|
||
protected IActionResult HandleModelStateErrors()
|
||
{
|
||
var errors = ModelState
|
||
.Where(x => x.Value?.Errors.Count > 0)
|
||
.ToDictionary(
|
||
kvp => kvp.Key,
|
||
kvp => kvp.Value?.Errors.Select(e => e.ErrorMessage).ToArray() ?? Array.Empty<string>()
|
||
);
|
||
|
||
return ErrorResponse("VALIDATION_ERROR", "輸入資料驗證失敗", errors, 400);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根據錯誤代碼獲取建議
|
||
/// </summary>
|
||
private List<string> GetSuggestionsForError(string errorCode)
|
||
{
|
||
return errorCode switch
|
||
{
|
||
"VALIDATION_ERROR" => new List<string> { "請檢查輸入格式", "確保所有必填欄位已填寫" },
|
||
"INVALID_INPUT" => new List<string> { "請檢查輸入格式", "確保文本長度在限制內" },
|
||
"RATE_LIMIT_EXCEEDED" => new List<string> { "升級到Premium帳戶以獲得無限使用", "明天重新嘗試" },
|
||
"AI_SERVICE_ERROR" => new List<string> { "請稍後重試", "如果問題持續,請聯繫客服" },
|
||
"UNAUTHORIZED" => new List<string> { "請檢查登入狀態", "確認Token是否有效" },
|
||
"NOT_FOUND" => new List<string> { "請檢查資源ID是否正確", "確認資源是否存在" },
|
||
_ => new List<string> { "請稍後重試", "如果問題持續,請聯繫客服" }
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 統一API響應格式
|
||
/// </summary>
|
||
public class ApiResponse<T>
|
||
{
|
||
public bool Success { get; set; } = true;
|
||
public T? Data { get; set; }
|
||
public string? Message { get; set; }
|
||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分頁響應格式
|
||
/// </summary>
|
||
public class PagedApiResponse<T> : ApiResponse<List<T>>
|
||
{
|
||
public PaginationMetadata Pagination { get; set; } = new();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分頁元數據
|
||
/// </summary>
|
||
public class PaginationMetadata
|
||
{
|
||
public int CurrentPage { get; set; }
|
||
public int PageSize { get; set; }
|
||
public int TotalCount { get; set; }
|
||
public int TotalPages { get; set; }
|
||
public bool HasNext { get; set; }
|
||
public bool HasPrevious { get; set; }
|
||
} |