fengling-console/Controllers/TenantsController.cs
Sam c8cb7c06bc feat: 添加Console API认证和OpenIddict集成
- 配置AuthService使用OpenIddict reference tokens
- 添加fengling-api客户端用于introspection验证
- 配置Console API通过OpenIddict验证reference tokens
- 实现Tenant/Users/Roles/OAuthClients CRUD API
- 添加GatewayController服务注册API
- 重构Repository和Service层支持多租户

BREAKING CHANGE: API认证现在使用OpenIddict reference tokens
2026-02-08 19:01:25 +08:00

301 lines
11 KiB
C#

namespace Fengling.Console.Controllers;
/// <summary>
/// 租户管理控制器
/// 提供租户的增删改查以及租户用户、角色、配置管理功能
/// </summary>
[ApiController]
[Route("api/console/[controller]")]
[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
public class TenantsController : ControllerBase
{
private readonly ITenantService _tenantService;
private readonly ILogger<TenantsController> _logger;
public TenantsController(ITenantService tenantService, ILogger<TenantsController> logger)
{
_tenantService = tenantService;
_logger = logger;
}
/// <summary>
/// 获取租户列表
/// </summary>
/// <param name="query">分页查询参数,支持按名称、租户编码和状态筛选</param>
/// <returns>分页的租户列表,包含租户基本信息和状态</returns>
/// <response code="200">成功返回租户分页列表</response>
/// <response code="500">服务器内部错误</response>
[HttpGet]
[Produces("application/json")]
[ProducesResponseType(typeof(PagedResultDto<TenantDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<PagedResultDto<TenantDto>>> GetTenants([FromQuery] TenantQueryDto query)
{
try
{
var (items, totalCount) = await _tenantService.GetTenantsAsync(query.Page, query.PageSize, query.Name,
query.TenantId, query.Status);
var result = new PagedResultDto<TenantDto>
{
Items = items.ToList(),
TotalCount = totalCount,
Page = query.Page,
PageSize = query.PageSize
};
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting tenants");
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 获取单个租户详情
/// </summary>
/// <param name="id">租户ID</param>
/// <returns>租户的详细信息,包括配置、限额等信息</returns>
/// <response code="200">成功返回租户详情</response>
/// <response code="404">租户不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpGet("{id}")]
[Produces("application/json")]
[ProducesResponseType(typeof(TenantDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<TenantDto>> GetTenant(long id)
{
try
{
var tenant = await _tenantService.GetTenantAsync(id);
if (tenant == null)
{
return NotFound();
}
return Ok(tenant);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting tenant {TenantId}", id);
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 获取指定租户的用户列表
/// </summary>
/// <param name="tenantId">租户ID</param>
/// <returns>属于该租户的所有用户列表</returns>
/// <response code="200">成功返回用户列表</response>
/// <response code="404">租户不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpGet("{tenantId}/users")]
[Produces("application/json")]
[ProducesResponseType(typeof(IEnumerable<UserDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<IEnumerable<UserDto>>> GetTenantUsers(long tenantId)
{
try
{
var users = await _tenantService.GetTenantUsersAsync(tenantId);
return Ok(users);
}
catch (KeyNotFoundException ex)
{
_logger.LogWarning(ex, "Tenant not found: {TenantId}", tenantId);
return NotFound();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting users for tenant {TenantId}", tenantId);
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 获取指定租户的角色列表
/// </summary>
/// <param name="tenantId">租户ID</param>
/// <returns>属于该租户的所有角色列表</returns>
/// <response code="200">成功返回角色列表</response>
/// <response code="404">租户不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpGet("{tenantId}/roles")]
[Produces("application/json")]
[ProducesResponseType(typeof(IEnumerable<object>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<IEnumerable<object>>> GetTenantRoles(long tenantId)
{
try
{
var roles = await _tenantService.GetTenantRolesAsync(tenantId);
return Ok(roles);
}
catch (KeyNotFoundException ex)
{
_logger.LogWarning(ex, "Tenant not found: {TenantId}", tenantId);
return NotFound();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting roles for tenant {TenantId}", tenantId);
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 获取租户配置信息
/// </summary>
/// <param name="id">租户ID</param>
/// <returns>租户的详细配置信息,包括功能开关、配额限制等</returns>
/// <response code="200">成功返回租户配置</response>
/// <response code="404">租户不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpGet("{id}/settings")]
[Produces("application/json")]
[ProducesResponseType(typeof(TenantSettingsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<TenantSettingsDto>> GetTenantSettings(long id)
{
try
{
var settings = await _tenantService.GetTenantSettingsAsync(id);
return Ok(settings);
}
catch (KeyNotFoundException ex)
{
_logger.LogWarning(ex, "Tenant not found: {TenantId}", id);
return NotFound();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting settings for tenant {TenantId}", id);
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 更新租户配置
/// </summary>
/// <param name="id">租户ID</param>
/// <param name="settings">需要更新的租户配置信息</param>
/// <returns>无内容响应</returns>
/// <response code="204">成功更新租户配置</response>
/// <response code="404">租户不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpPut("{id}/settings")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> UpdateTenantSettings(long id, [FromBody] TenantSettingsDto settings)
{
try
{
await _tenantService.UpdateTenantSettingsAsync(id, settings);
return NoContent();
}
catch (KeyNotFoundException ex)
{
_logger.LogWarning(ex, "Tenant not found: {TenantId}", id);
return NotFound();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating settings for tenant {TenantId}", id);
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 创建新租户
/// </summary>
/// <param name="dto">创建租户所需的配置信息</param>
/// <returns>创建的租户详情</returns>
/// <response code="201">成功创建租户</response>
/// <response code="500">服务器内部错误</response>
[HttpPost]
[Produces("application/json")]
[ProducesResponseType(typeof(TenantDto), StatusCodes.Status201Created)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<TenantDto>> CreateTenant([FromBody] CreateTenantDto dto)
{
try
{
var tenant = await _tenantService.CreateTenantAsync(dto);
return CreatedAtAction(nameof(GetTenant), new { id = tenant.Id }, tenant);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating tenant");
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 更新租户信息
/// </summary>
/// <param name="id">租户ID</param>
/// <param name="dto">需要更新的租户配置信息</param>
/// <returns>无内容响应</returns>
/// <response code="204">成功更新租户</response>
/// <response code="404">租户不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpPut("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> UpdateTenant(long id, [FromBody] UpdateTenantDto dto)
{
try
{
await _tenantService.UpdateTenantAsync(id, dto);
return NoContent();
}
catch (KeyNotFoundException ex)
{
_logger.LogWarning(ex, "Tenant not found: {TenantId}", id);
return NotFound();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating tenant {TenantId}", id);
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 删除租户
/// </summary>
/// <param name="id">租户ID</param>
/// <returns>无内容响应</returns>
/// <response code="204">成功删除租户</response>
/// <response code="404">租户不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpDelete("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> DeleteTenant(long id)
{
try
{
await _tenantService.DeleteTenantAsync(id);
return NoContent();
}
catch (KeyNotFoundException ex)
{
_logger.LogWarning(ex, "Tenant not found: {TenantId}", id);
return NotFound();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting tenant {TenantId}", id);
return StatusCode(500, new { message = ex.Message });
}
}
}