feat(saas): implement marketplace and tenant onboarding

This commit is contained in:
2026-07-29 13:58:59 +08:00
parent 76606029e2
commit 6db200a2fc
145 changed files with 16862 additions and 94529 deletions

View File

@@ -7,37 +7,76 @@ using Tiku.Domain.Content;
namespace Tiku.Api.Contracts;
/// <summary>
/// 内容管理查询参数。
/// </summary>
public sealed class ContentManagementQueryDto
{
/// <summary>
/// 地区 ID。
/// </summary>
public Guid? RegionId { get; set; }
/// <summary>
/// 内容入口 ID。
/// </summary>
public Guid? EntryId { get; set; }
/// <summary>
/// 节点 ID。
/// </summary>
public Guid? NodeId { get; set; }
/// <summary>
/// 题集 ID。
/// </summary>
public Guid? CollectionId { get; set; }
/// <summary>
/// 父节点 ID传 root 表示根节点。
/// </summary>
[StringLength(64)]
[RegularExpression("^(root|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$")]
public string? ParentId { get; set; }
/// <summary>
/// 入口类型。
/// </summary>
[StringLength(50)]
public string? EntryType { get; set; }
/// <summary>
/// 题集类型。
/// </summary>
[StringLength(50)]
public string? CollectionType { get; set; }
/// <summary>
/// 模式。
/// </summary>
[StringLength(50)]
public string? Mode { get; set; }
/// <summary>
/// 标记类型。
/// </summary>
[StringLength(50)]
public string? MarkerType { get; set; }
/// <summary>
/// 关键字。
/// </summary>
[StringLength(100)]
public string? Keyword { get; set; }
/// <summary>
/// 是否包含停用数据。
/// </summary>
public bool IncludeInactive { get; set; }
/// <summary>
/// 返回数量上限。
/// </summary>
[Range(1, 1000)]
public int? Limit { get; set; }
@@ -59,43 +98,88 @@ public sealed class ContentManagementQueryDto
}
}
/// <summary>
/// 新增或更新内容条目请求 DTO。
/// </summary>
public sealed class UpsertContentEntryDto
{
/// <summary>
/// ID。
/// </summary>
public Guid? Id { get; set; }
/// <summary>
/// 地区 ID。
/// </summary>
public Guid? RegionId { get; set; }
/// <summary>
/// 历史系统 ID。
/// </summary>
[StringLength(64)]
public string? LegacyId { get; set; }
/// <summary>
/// 入口键。
/// </summary>
[StringLength(100)]
public string? EntryKey { get; set; }
/// <summary>
/// 名称。
/// </summary>
[Required]
[StringLength(300)]
public string Name { get; set; } = string.Empty;
/// <summary>
/// 入口类型。
/// </summary>
[StringLength(50)]
public string? EntryType { get; set; }
/// <summary>
/// 图标。
/// </summary>
[StringLength(100)]
public string? Icon { get; set; }
/// <summary>
/// 路由地址。
/// </summary>
[StringLength(500)]
public string? Route { get; set; }
/// <summary>
/// 说明。
/// </summary>
[StringLength(2000)]
public string? Description { get; set; }
/// <summary>
/// 可见性。
/// </summary>
[StringLength(50)]
public string? Visibility { get; set; }
/// <summary>
/// 访问规则。
/// </summary>
public JsonElement AccessRules { get; set; } = JsonDefaults.Object();
/// <summary>
/// 布局配置。
/// </summary>
public JsonElement LayoutConfig { get; set; } = JsonDefaults.Object();
/// <summary>
/// 显示顺序。
/// </summary>
public int? Order { get; set; }
/// <summary>
/// 是否启用。
/// </summary>
public bool? IsActive { get; set; }
public UpsertContentEntryCommand ToCommand()
@@ -118,45 +202,96 @@ public sealed class UpsertContentEntryDto
}
}
/// <summary>
/// 新增或更新内容节点请求 DTO。
/// </summary>
public sealed class UpsertContentNodeDto
{
/// <summary>
/// ID。
/// </summary>
public Guid? Id { get; set; }
/// <summary>
/// 内容入口 ID。
/// </summary>
[Required]
public Guid EntryId { get; set; }
/// <summary>
/// 地区 ID。
/// </summary>
public Guid? RegionId { get; set; }
/// <summary>
/// 父节点 ID。
/// </summary>
public Guid? ParentId { get; set; }
/// <summary>
/// 历史系统 ID。
/// </summary>
[StringLength(64)]
public string? LegacyId { get; set; }
/// <summary>
/// 节点键。
/// </summary>
[StringLength(100)]
public string? NodeKey { get; set; }
/// <summary>
/// 名称。
/// </summary>
[Required]
[StringLength(300)]
public string Name { get; set; } = string.Empty;
/// <summary>
/// 节点Type。
/// </summary>
[StringLength(50)]
public string? NodeType { get; set; }
/// <summary>
/// 标记类型。
/// </summary>
[StringLength(50)]
public string? MarkerType { get; set; }
/// <summary>
/// 标记配置。
/// </summary>
public JsonElement MarkerConfig { get; set; } = JsonDefaults.Object();
/// <summary>
/// 显示顺序。
/// </summary>
public int? Order { get; set; }
/// <summary>
/// 是否启用。
/// </summary>
public bool? IsActive { get; set; }
/// <summary>
/// 是否可选择。
/// </summary>
public bool? IsSelectable { get; set; }
/// <summary>
/// 是否叶子节点。
/// </summary>
public bool? IsLeaf { get; set; }
/// <summary>
/// 访问规则。
/// </summary>
public JsonElement AccessRules { get; set; } = JsonDefaults.Object();
/// <summary>
/// 扩展元数据。
/// </summary>
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
public UpsertContentNodeCommand ToCommand()
@@ -181,48 +316,105 @@ public sealed class UpsertContentNodeDto
}
}
/// <summary>
/// 新增或更新题目题集请求 DTO。
/// </summary>
public sealed class UpsertQuestionCollectionDto
{
/// <summary>
/// ID。
/// </summary>
public Guid? Id { get; set; }
/// <summary>
/// 地区 ID。
/// </summary>
public Guid? RegionId { get; set; }
/// <summary>
/// 内容入口 ID。
/// </summary>
public Guid? EntryId { get; set; }
/// <summary>
/// 节点 ID。
/// </summary>
public Guid? NodeId { get; set; }
/// <summary>
/// 科目 ID。
/// </summary>
public Guid? SubjectId { get; set; }
/// <summary>
/// 分类 ID。
/// </summary>
public Guid? CategoryId { get; set; }
/// <summary>
/// 题库 ID。
/// </summary>
public Guid? QuestionBankId { get; set; }
/// <summary>
/// 历史系统 ID。
/// </summary>
[StringLength(64)]
public string? LegacyId { get; set; }
/// <summary>
/// 名称。
/// </summary>
[Required]
[StringLength(300)]
public string Name { get; set; } = string.Empty;
/// <summary>
/// 题集类型。
/// </summary>
[StringLength(50)]
public string? CollectionType { get; set; }
/// <summary>
/// 来源类型。
/// </summary>
[StringLength(50)]
public string? SourceType { get; set; }
/// <summary>
/// 筛选条件。
/// </summary>
public JsonElement Filters { get; set; } = JsonDefaults.Object();
/// <summary>
/// 总分。
/// </summary>
public decimal? TotalScore { get; set; }
/// <summary>
/// 时长,单位为分钟。
/// </summary>
public int? DurationMinutes { get; set; }
/// <summary>
/// 状态。
/// </summary>
[StringLength(50)]
public string? Status { get; set; }
/// <summary>
/// 显示顺序。
/// </summary>
public int? Order { get; set; }
/// <summary>
/// 访问规则。
/// </summary>
public JsonElement AccessRules { get; set; } = JsonDefaults.Object();
/// <summary>
/// 扩展元数据。
/// </summary>
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
public UpsertQuestionCollectionCommand ToCommand()
@@ -249,23 +441,47 @@ public sealed class UpsertQuestionCollectionDto
}
}
/// <summary>
/// 题集题目请求 DTO。
/// </summary>
public sealed class CollectionQuestionDto
{
/// <summary>
/// 题目 ID。
/// </summary>
[Required]
public Guid QuestionId { get; set; }
/// <summary>
/// 来源。
/// </summary>
[Required]
public QuestionSource Source { get; set; } = QuestionSource.Tenant;
/// <summary>
/// 分段键。
/// </summary>
[StringLength(100)]
public string? SectionKey { get; set; }
/// <summary>
/// 显示顺序。
/// </summary>
public int? Order { get; set; }
/// <summary>
/// 分数。
/// </summary>
public decimal? Score { get; set; }
/// <summary>
/// 是否必填。
/// </summary>
public bool? Required { get; set; }
/// <summary>
/// 扩展元数据。
/// </summary>
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
public CollectionQuestionCommand ToCommand()
@@ -280,11 +496,20 @@ public sealed class CollectionQuestionDto
}
}
/// <summary>
/// 替换题集Items请求 DTO。
/// </summary>
public sealed class ReplaceCollectionItemsDto
{
/// <summary>
/// 题集 ID。
/// </summary>
[Required]
public Guid CollectionId { get; set; }
/// <summary>
/// 题目列表。
/// </summary>
public IReadOnlyCollection<CollectionQuestionDto> Questions { get; set; } = [];
public ReplaceCollectionItemsCommand ToCommand()
@@ -295,48 +520,105 @@ public sealed class ReplaceCollectionItemsDto
}
}
/// <summary>
/// 新增或更新练习Blueprint请求 DTO。
/// </summary>
public sealed class UpsertPracticeBlueprintDto
{
/// <summary>
/// ID。
/// </summary>
public Guid? Id { get; set; }
/// <summary>
/// 地区 ID。
/// </summary>
public Guid? RegionId { get; set; }
/// <summary>
/// 内容入口 ID。
/// </summary>
public Guid? EntryId { get; set; }
/// <summary>
/// 节点 ID。
/// </summary>
public Guid? NodeId { get; set; }
/// <summary>
/// 题集 ID。
/// </summary>
public Guid? CollectionId { get; set; }
/// <summary>
/// 历史系统 ID。
/// </summary>
[StringLength(64)]
public string? LegacyId { get; set; }
/// <summary>
/// 名称。
/// </summary>
[Required]
[StringLength(300)]
public string Name { get; set; } = string.Empty;
/// <summary>
/// 模式。
/// </summary>
[StringLength(50)]
public string? Mode { get; set; }
/// <summary>
/// 组卷方式。
/// </summary>
[StringLength(50)]
public string? AssemblyType { get; set; }
/// <summary>
/// 题目数量上限。
/// </summary>
public int? QuestionLimit { get; set; }
/// <summary>
/// 时长,单位为分钟。
/// </summary>
public int? DurationMinutes { get; set; }
/// <summary>
/// 总分。
/// </summary>
public decimal? TotalScore { get; set; }
/// <summary>
/// 及格分。
/// </summary>
public decimal? PassScore { get; set; }
/// <summary>
/// 分段配置。
/// </summary>
public JsonElement Sections { get; set; } = JsonDefaults.Array();
/// <summary>
/// 规则配置。
/// </summary>
public JsonElement Rules { get; set; } = JsonDefaults.Object();
/// <summary>
/// 访问规则。
/// </summary>
public JsonElement AccessRules { get; set; } = JsonDefaults.Object();
/// <summary>
/// 状态。
/// </summary>
[StringLength(50)]
public string? Status { get; set; }
/// <summary>
/// 显示顺序。
/// </summary>
public int? Order { get; set; }
public UpsertPracticeBlueprintCommand ToCommand()
@@ -363,12 +645,21 @@ public sealed class UpsertPracticeBlueprintDto
}
}
/// <summary>
/// 导入Template查询参数。
/// </summary>
public sealed class ImportTemplateQueryDto
{
/// <summary>
/// 导入Type。
/// </summary>
[Required]
[StringLength(50)]
public string ImportType { get; set; } = string.Empty;
/// <summary>
/// 导出格式。
/// </summary>
[StringLength(10)]
public string? Format { get; set; }
}