using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Tiku.Api.Contracts; using Tiku.Application.Catalog; using Tiku.Application.Security; using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Persistence; namespace Tiku.Api.Controllers; [ApiController] [AllowAnonymous] [Produces("application/json")] [Route("api/catalog")] public sealed class CatalogController( ICatalogQueryService catalogQueryService, ICurrentTenant currentTenant, TikuDbContext dbContext) : ControllerBase { [HttpGet("regions")] [EndpointSummary("查询可用地区")] [ProducesResponseType>(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetRegions( [FromQuery] CatalogQueryDto query, CancellationToken cancellationToken) { return Ok(await catalogQueryService.GetRegionsAsync( query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)), cancellationToken)); } [HttpGet("region-modules")] [EndpointSummary("查询地区功能模块")] [ProducesResponseType>(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetRegionModules( [FromQuery] CatalogQueryDto query, CancellationToken cancellationToken) { return Ok(await catalogQueryService.GetRegionModulesAsync( query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)), cancellationToken)); } [HttpGet("module-nodes")] [EndpointSummary("查询模块导航节点")] [ProducesResponseType>(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetModuleNodes( [FromQuery] CatalogQueryDto query, CancellationToken cancellationToken) { return Ok(await catalogQueryService.GetModuleNodesAsync( query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)), cancellationToken)); } [HttpGet("schools")] [EndpointSummary("查询院校目录")] [ProducesResponseType>(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetSchools( [FromQuery] CatalogQueryDto query, CancellationToken cancellationToken) { return Ok(await catalogQueryService.GetSchoolsAsync( query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)), cancellationToken)); } [HttpGet("majors")] [EndpointSummary("查询专业目录")] [ProducesResponseType>(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetMajors( [FromQuery] CatalogQueryDto query, CancellationToken cancellationToken) { return Ok(await catalogQueryService.GetMajorsAsync( query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)), cancellationToken)); } [HttpGet("subjects")] [EndpointSummary("查询科目目录")] [ProducesResponseType>(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetSubjects( [FromQuery] CatalogQueryDto query, CancellationToken cancellationToken) { return Ok(await catalogQueryService.GetSubjectsAsync( query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)), cancellationToken)); } [HttpGet("categories")] [HttpGet("question-categories")] [EndpointSummary("查询题目分类")] [ProducesResponseType>(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetCategories( [FromQuery] CatalogQueryDto query, CancellationToken cancellationToken) { return Ok(await catalogQueryService.GetCategoriesAsync( query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)), cancellationToken)); } private async Task ResolveTenantIdAsync( CatalogQueryDto query, CancellationToken cancellationToken) { if (currentTenant.TenantId.HasValue) { return currentTenant.TenantId.Value; } var tenantCode = query.TenantCode ?? Request.Headers["x-tenant-code"].FirstOrDefault(); if (string.IsNullOrWhiteSpace(tenantCode)) { throw new TenantNotFoundException(); } var tenantId = await dbContext.Tenants .Where(tenant => tenant.Slug == tenantCode.Trim() && tenant.Status == TenantStatus.Active) .Select(tenant => (Guid?)tenant.Id) .SingleOrDefaultAsync(cancellationToken); return tenantId ?? throw new TenantNotFoundException(); } } public sealed class TenantNotFoundException : Exception { public TenantNotFoundException() : base("Tenant was not found.") { } }