forked from xiongyuxing/tiku-backend.net
- Added tags to BrowserAuthController for browser authentication endpoints. - Added tags to CatalogController for public catalog access. - Added tags to CommerceController for student transaction operations. - Added tags to CommissionController for tenant commission management. - Added tags to CrmController for tenant CRM functionalities. - Added tags to HealthController for system health checks. - Added tags to LearningController for student learning resources. - Added tags to MeController for current user information. - Added tags to PlatformAdminController for platform management. - Introduced PlatformBackofficeController for backend permissions management. - Added tags to PlatformBillingCallbackController for billing callbacks. - Added tags to PlatformPaymentSettingsController for payment settings management. - Added tags to PlatformSaasController for SaaS package management. - Added tags to PlatformTenantCapabilitiesController for tenant capabilities. - Added tags to PointsController for student points management. - Added tags to ProfileController for student profile management. - Added tags to QuestionVideosController for question video resources. - Added tags to ReferralController for referral growth management. - Added tags to RuntimeController for runtime configurations. - Added tags to ScorelineController for scoreline management. - Added tags to TaxonomyController for category management. - Added tags to TenantAdminDirectController for tenant operations management. - Introduced TenantBackofficeController for tenant backend permissions. - Added tags to TenantBillingController for tenant billing operations. - Added tags to TenantCommerceController for tenant commerce operations. - Added tags to TenantContentController for tenant content management. - Added tags to TenantContentDirectController for direct content management. - Added tags to TenantFrontendConfigController for frontend configurations. - Added tags to TenantOnboardingController for onboarding guidance. - Added tags to TenantPublicController for public tenant configurations. - Added tags to TenantsController for current tenant information. - Added tags to VideosController for student video resources.
553 lines
24 KiB
C#
553 lines
24 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Tiku.Application.Assets;
|
|
using Tiku.Api.Contracts;
|
|
using Tiku.Application.Catalog;
|
|
using Tiku.Application.Content;
|
|
using Tiku.Application.QuestionBanks;
|
|
using Tiku.Application.Security;
|
|
using Tiku.Application.StudyContent;
|
|
using Tiku.Domain.Tenancy;
|
|
using Tiku.Infrastructure.Persistence;
|
|
|
|
namespace Tiku.Api.Controllers;
|
|
|
|
[ApiController]
|
|
[Tags("学生端-公开目录")]
|
|
[AllowAnonymous]
|
|
[Produces("application/json")]
|
|
[Route("api/catalog")]
|
|
public sealed class CatalogController(
|
|
ICatalogQueryService catalogQueryService,
|
|
IContentNavigationQueryService contentNavigationQueryService,
|
|
IQuestionBankQueryService questionBankQueryService,
|
|
IStudyContentQueryService studyContentQueryService,
|
|
IAssetQueryService assetQueryService,
|
|
ITenantContext currentTenant,
|
|
TikuDbContext dbContext) : ControllerBase
|
|
{
|
|
[HttpGet("regions")]
|
|
[EndpointSummary("查询可用地区")]
|
|
[ProducesResponseType<CatalogList<RegionCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<RegionCatalogItem>>> GetRegions(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetRegionsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("region-modules")]
|
|
[EndpointSummary("查询地区功能模块")]
|
|
[ProducesResponseType<CatalogList<RegionModuleCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<RegionModuleCatalogItem>>> GetRegionModules(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetRegionModulesAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("module-nodes")]
|
|
[EndpointSummary("查询模块导航节点")]
|
|
[ProducesResponseType<CatalogList<ModuleNodeCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<ModuleNodeCatalogItem>>> GetModuleNodes(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetModuleNodesAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("schools")]
|
|
[EndpointSummary("查询院校目录")]
|
|
[ProducesResponseType<CatalogList<SchoolCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<SchoolCatalogItem>>> GetSchools(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetSchoolsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("majors")]
|
|
[EndpointSummary("查询专业目录")]
|
|
[ProducesResponseType<CatalogList<MajorCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<MajorCatalogItem>>> GetMajors(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetMajorsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("subjects")]
|
|
[EndpointSummary("查询科目目录")]
|
|
[ProducesResponseType<CatalogList<SubjectCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<SubjectCatalogItem>>> 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<CatalogList<CategoryCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<CategoryCatalogItem>>> GetCategories(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetCategoriesAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("content-entries")]
|
|
[EndpointSummary("查询内容入口")]
|
|
[ProducesResponseType<CatalogList<ContentEntryCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<ContentEntryCatalogItem>>> GetContentEntries(
|
|
[FromQuery] ContentNavigationQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await contentNavigationQueryService.GetContentEntriesAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("content-nodes")]
|
|
[EndpointSummary("查询内容导航节点")]
|
|
[ProducesResponseType<CatalogList<ContentNodeCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<ContentNodeCatalogItem>>> GetContentNodes(
|
|
[FromQuery] ContentNavigationQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await contentNavigationQueryService.GetContentNodesAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("question-collections")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Practice)]
|
|
[EndpointSummary("查询可用题集")]
|
|
[ProducesResponseType<CatalogList<QuestionCollectionCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<QuestionCollectionCatalogItem>>> GetQuestionCollections(
|
|
[FromQuery] ContentNavigationQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await contentNavigationQueryService.GetQuestionCollectionsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("question-collections/questions")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Practice)]
|
|
[EndpointSummary("查询题集内题目")]
|
|
[ProducesResponseType<CatalogList<CollectionQuestionCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<CollectionQuestionCatalogItem>>> GetCollectionQuestions(
|
|
[FromQuery] ContentNavigationQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await contentNavigationQueryService.GetCollectionQuestionsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("practice-blueprints")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Practice)]
|
|
[EndpointSummary("查询练习蓝图")]
|
|
[ProducesResponseType<CatalogList<PracticeBlueprintCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<PracticeBlueprintCatalogItem>>> GetPracticeBlueprints(
|
|
[FromQuery] ContentNavigationQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await contentNavigationQueryService.GetPracticeBlueprintsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("question-banks")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Practice)]
|
|
[EndpointSummary("查询题库列表")]
|
|
[ProducesResponseType<CatalogList<QuestionBankCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<QuestionBankCatalogItem>>> GetQuestionBanks(
|
|
[FromQuery] QuestionBankQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await questionBankQueryService.GetQuestionBanksAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("questions")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Practice)]
|
|
[EndpointSummary("查询已发布题目")]
|
|
[EndpointDescription("支持按题库、科目、分类、模块节点、内容入口、内容节点、题集或题目 ID 列表筛选。")]
|
|
[ProducesResponseType<CatalogList<QuestionCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<QuestionCatalogItem>>> GetQuestions(
|
|
[FromQuery] QuestionBankQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await questionBankQueryService.GetQuestionsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("questions/{questionId:guid}")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Practice)]
|
|
[EndpointSummary("查询题目详情")]
|
|
[ProducesResponseType<QuestionCatalogItem>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<QuestionCatalogItem>> GetQuestion(
|
|
Guid questionId,
|
|
[FromQuery] QuestionBankQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await questionBankQueryService.GetQuestionAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken), questionId),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("questions/{questionId:guid}/versions")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Practice)]
|
|
[EndpointSummary("查询题目版本")]
|
|
[ProducesResponseType<CatalogList<QuestionVersionCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<QuestionVersionCatalogItem>>> GetQuestionVersions(
|
|
Guid questionId,
|
|
[FromQuery] QuestionBankQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await questionBankQueryService.GetQuestionVersionsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken), questionId),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("vocabulary-units")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Vocabulary)]
|
|
[EndpointSummary("查询词汇单元")]
|
|
[ProducesResponseType<CatalogList<VocabularyUnitCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<VocabularyUnitCatalogItem>>> GetVocabularyUnits(
|
|
[FromQuery] StudyContentQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await studyContentQueryService.GetVocabularyUnitsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("vocabulary-words")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Vocabulary)]
|
|
[EndpointSummary("查询词汇单词")]
|
|
[ProducesResponseType<CatalogList<VocabularyWordCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<VocabularyWordCatalogItem>>> GetVocabularyWords(
|
|
[FromQuery] StudyContentQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await studyContentQueryService.GetVocabularyWordsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("handbook-subjects")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Handbook)]
|
|
[EndpointSummary("查询知识手册科目")]
|
|
[ProducesResponseType<CatalogList<HandbookSubjectCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<HandbookSubjectCatalogItem>>> GetHandbookSubjects(
|
|
[FromQuery] StudyContentQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await studyContentQueryService.GetHandbookSubjectsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("handbook-chapters")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Handbook)]
|
|
[EndpointSummary("查询知识手册章节")]
|
|
[ProducesResponseType<CatalogList<HandbookChapterCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<HandbookChapterCatalogItem>>> GetHandbookChapters(
|
|
[FromQuery] StudyContentQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await studyContentQueryService.GetHandbookChaptersAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("handbook-entries")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Handbook)]
|
|
[EndpointSummary("查询知识手册条目")]
|
|
[ProducesResponseType<CatalogList<HandbookEntryCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<HandbookEntryCatalogItem>>> GetHandbookEntries(
|
|
[FromQuery] StudyContentQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await studyContentQueryService.GetHandbookEntriesAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("content-assets")]
|
|
[EndpointSummary("查询内容资源")]
|
|
[ProducesResponseType<CatalogList<ContentAssetCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<ContentAssetCatalogItem>>> GetContentAssets(
|
|
[FromQuery] AssetQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await assetQueryService.GetContentAssetsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("images")]
|
|
[EndpointSummary("查询图片资源")]
|
|
[ProducesResponseType<CatalogList<ImageAssetCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<ImageAssetCatalogItem>>> GetImages(
|
|
[FromQuery] AssetQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await assetQueryService.GetImagesAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("app-assets")]
|
|
[EndpointSummary("查询应用资源")]
|
|
[ProducesResponseType<CatalogList<AppAssetCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<AppAssetCatalogItem>>> GetAppAssets(
|
|
[FromQuery] AssetQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await assetQueryService.GetAppAssetsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("video-explanations")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Video)]
|
|
[EndpointSummary("查询视频讲解")]
|
|
[ProducesResponseType<CatalogList<VideoExplanationCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<VideoExplanationCatalogItem>>> GetVideoExplanations(
|
|
[FromQuery] AssetQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await assetQueryService.GetVideoExplanationsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("question-videos")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Video)]
|
|
[EndpointSummary("查询题目关联视频")]
|
|
[ProducesResponseType<CatalogList<QuestionVideoCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<QuestionVideoCatalogItem>>> GetQuestionVideos(
|
|
[FromQuery] AssetQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await assetQueryService.GetQuestionVideosAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("banners")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.SiteContent)]
|
|
[EndpointSummary("查询首页横幅")]
|
|
[ProducesResponseType<CatalogList<BannerCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<BannerCatalogItem>>> GetBanners(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetBannersAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("faqs")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.SiteContent)]
|
|
[EndpointSummary("查询常见问题")]
|
|
[ProducesResponseType<CatalogList<FaqCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<FaqCatalogItem>>> GetFaqs(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetFaqsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("announcements")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.SiteContent)]
|
|
[EndpointSummary("查询公告")]
|
|
[ProducesResponseType<CatalogList<AnnouncementCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<AnnouncementCatalogItem>>> GetAnnouncements(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetAnnouncementsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("exam-dates")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.SiteContent)]
|
|
[EndpointSummary("查询考试日期")]
|
|
[ProducesResponseType<CatalogList<ExamDateCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<ExamDateCatalogItem>>> GetExamDates(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetExamDatesAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("products")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.StudentStore)]
|
|
[EndpointSummary("查询可购买产品")]
|
|
[ProducesResponseType<CatalogList<ProductCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<ProductCatalogItem>>> GetProducts(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetProductsAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
[HttpGet("svip-plans")]
|
|
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.StudentStore)]
|
|
[EndpointSummary("查询 SVIP 套餐")]
|
|
[ProducesResponseType<CatalogList<SvipPlanCatalogItem>>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<CatalogList<SvipPlanCatalogItem>>> GetSvipPlans(
|
|
[FromQuery] CatalogQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await catalogQueryService.GetSvipPlansAsync(
|
|
query.ToFilter(await ResolveTenantIdAsync(query, cancellationToken)),
|
|
cancellationToken));
|
|
}
|
|
|
|
private async Task<Guid> 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();
|
|
}
|
|
|
|
private Task<Guid> ResolveTenantIdAsync(
|
|
ContentNavigationQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return ResolveTenantIdAsync(
|
|
new CatalogQueryDto
|
|
{
|
|
TenantCode = query.TenantCode
|
|
},
|
|
cancellationToken);
|
|
}
|
|
|
|
private Task<Guid> ResolveTenantIdAsync(
|
|
QuestionBankQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return ResolveTenantIdAsync(
|
|
new CatalogQueryDto
|
|
{
|
|
TenantCode = query.TenantCode
|
|
},
|
|
cancellationToken);
|
|
}
|
|
|
|
private Task<Guid> ResolveTenantIdAsync(
|
|
StudyContentQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return ResolveTenantIdAsync(
|
|
new CatalogQueryDto
|
|
{
|
|
TenantCode = query.TenantCode
|
|
},
|
|
cancellationToken);
|
|
}
|
|
|
|
private Task<Guid> ResolveTenantIdAsync(
|
|
AssetQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return ResolveTenantIdAsync(
|
|
new CatalogQueryDto
|
|
{
|
|
TenantCode = query.TenantCode
|
|
},
|
|
cancellationToken);
|
|
}
|
|
}
|
|
|
|
public sealed class TenantNotFoundException : Exception
|
|
{
|
|
public TenantNotFoundException()
|
|
: base("Tenant was not found.")
|
|
{
|
|
}
|
|
}
|