666 lines
14 KiB
C#
666 lines
14 KiB
C#
using System.ComponentModel.DataAnnotations;
|
||
using System.Text.Json;
|
||
using Tiku.Application.Content;
|
||
using Tiku.Application.QuestionBanks;
|
||
using Tiku.Domain.Common;
|
||
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; }
|
||
|
||
public ContentManagementFilter ToFilter()
|
||
{
|
||
return new ContentManagementFilter(
|
||
RegionId,
|
||
EntryId,
|
||
NodeId,
|
||
CollectionId,
|
||
ParentId,
|
||
EntryType,
|
||
CollectionType,
|
||
Mode,
|
||
MarkerType,
|
||
Keyword,
|
||
IncludeInactive,
|
||
Limit);
|
||
}
|
||
}
|
||
|
||
/// <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()
|
||
{
|
||
return new UpsertContentEntryCommand(
|
||
Id,
|
||
RegionId,
|
||
LegacyId,
|
||
EntryKey,
|
||
Name,
|
||
EntryType,
|
||
Icon,
|
||
Route,
|
||
Description,
|
||
Visibility,
|
||
AccessRules,
|
||
LayoutConfig,
|
||
Order,
|
||
IsActive);
|
||
}
|
||
}
|
||
|
||
/// <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()
|
||
{
|
||
return new UpsertContentNodeCommand(
|
||
Id,
|
||
EntryId,
|
||
RegionId,
|
||
ParentId,
|
||
LegacyId,
|
||
NodeKey,
|
||
Name,
|
||
NodeType,
|
||
MarkerType,
|
||
MarkerConfig,
|
||
Order,
|
||
IsActive,
|
||
IsSelectable,
|
||
IsLeaf,
|
||
AccessRules,
|
||
Metadata);
|
||
}
|
||
}
|
||
|
||
/// <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()
|
||
{
|
||
return new UpsertQuestionCollectionCommand(
|
||
Id,
|
||
RegionId,
|
||
EntryId,
|
||
NodeId,
|
||
SubjectId,
|
||
CategoryId,
|
||
QuestionBankId,
|
||
LegacyId,
|
||
Name,
|
||
CollectionType,
|
||
SourceType,
|
||
Filters,
|
||
TotalScore,
|
||
DurationMinutes,
|
||
Status,
|
||
Order,
|
||
AccessRules,
|
||
Metadata);
|
||
}
|
||
}
|
||
|
||
/// <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()
|
||
{
|
||
return new CollectionQuestionCommand(
|
||
new QuestionLocator(Source, QuestionId),
|
||
SectionKey,
|
||
Order,
|
||
Score,
|
||
Required,
|
||
Metadata);
|
||
}
|
||
}
|
||
|
||
/// <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()
|
||
{
|
||
return new ReplaceCollectionItemsCommand(
|
||
CollectionId,
|
||
Questions.Select(question => question.ToCommand()).ToArray());
|
||
}
|
||
}
|
||
|
||
/// <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()
|
||
{
|
||
return new UpsertPracticeBlueprintCommand(
|
||
Id,
|
||
RegionId,
|
||
EntryId,
|
||
NodeId,
|
||
CollectionId,
|
||
LegacyId,
|
||
Name,
|
||
Mode,
|
||
AssemblyType,
|
||
QuestionLimit,
|
||
DurationMinutes,
|
||
TotalScore,
|
||
PassScore,
|
||
Sections,
|
||
Rules,
|
||
AccessRules,
|
||
Status,
|
||
Order);
|
||
}
|
||
}
|
||
|
||
/// <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; }
|
||
}
|