fengling-console/Controllers/TenantsController.cs
movingsam 74122b2c8c feat(auth): extract Tenant to Platform domain
- Add Fengling.Platform domain and infrastructure projects
- Move Tenant aggregate from AuthService/Console to Platform.Domain
- Add TenantRepository and SeedData to Platform
- Remove duplicate Tenant/TenantInfo models from AuthService and Console
- Update controllers and services to use Platform.Domain.Tenant
- Add new migrations for PlatformDbContext

BREAKING CHANGE: Tenant entity now uses strongly-typed ID (TenantId)
2026-02-18 23:02:03 +08:00

337 lines
13 KiB
C#

namespace Fengling.Console.Controllers;
using Fengling.Console.Services;
/// <summary>
/// 租户管理控制器
/// 提供租户的增删改查以及租户用户、角色、配置管理功能
/// </summary>
[ApiController]
[Route("api/console/[controller]")]
[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
public class TenantsController : ControllerBase
{
private readonly ITenantService _tenantService;
private readonly IH5LinkService _h5LinkService;
private readonly ILogger<TenantsController> _logger;
public TenantsController(ITenantService tenantService, IH5LinkService h5LinkService, ILogger<TenantsController> logger)
{
_tenantService = tenantService;
_h5LinkService = h5LinkService;
_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 });
}
}
/// <summary>
/// 生成H5访问链接和二维码
/// </summary>
/// <param name="id">租户ID</param>
/// <returns>H5链接和二维码Base64</returns>
/// <response code="200">成功返回链接和二维码</response>
/// <response code="404">租户不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpGet("{id}/h5-link")]
[Produces("application/json")]
[ProducesResponseType(typeof(H5LinkResult), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<H5LinkResult>> GetH5Link(long id)
{
try
{
var result = await _h5LinkService.GenerateH5LinkAsync(id);
return Ok(result);
}
catch (KeyNotFoundException ex)
{
_logger.LogWarning(ex, "Tenant not found: {TenantId}", id);
return NotFound(new { message = ex.Message });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error generating H5 link for tenant {TenantId}", id);
return StatusCode(500, new { message = "Failed to generate H5 link" });
}
}
}