refactor(learning): replace legacy session targets
This commit is contained in:
@@ -46,11 +46,6 @@ public sealed class PracticeSessionQueryDto
|
||||
/// </summary>
|
||||
public Guid? PracticeSessionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 练习蓝图 ID。
|
||||
/// </summary>
|
||||
public Guid? BlueprintId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模式。
|
||||
/// </summary>
|
||||
@@ -73,7 +68,6 @@ public sealed class PracticeSessionQueryDto
|
||||
{
|
||||
return new PracticeSessionFilter(
|
||||
routePracticeSessionId ?? PracticeSessionId,
|
||||
BlueprintId,
|
||||
Mode,
|
||||
Status,
|
||||
Limit);
|
||||
|
||||
@@ -105,7 +105,6 @@ public sealed record PracticeSessionCommand(
|
||||
|
||||
public sealed record PracticeSessionFilter(
|
||||
Guid? PracticeSessionId = null,
|
||||
Guid? BlueprintId = null,
|
||||
string? Mode = null,
|
||||
string? Status = null,
|
||||
int? Limit = null);
|
||||
@@ -167,12 +166,8 @@ public sealed record LearningActionResult(bool Ok, bool? IsFavorite = null);
|
||||
public sealed record PracticeSessionItem(
|
||||
Guid Id,
|
||||
string Mode,
|
||||
string? TargetType,
|
||||
Guid? TargetId,
|
||||
Guid? BlueprintId,
|
||||
Guid? CollectionId,
|
||||
Guid? EntryId,
|
||||
Guid? ContentNodeId,
|
||||
AccessResourceType ResourceType,
|
||||
Guid ResourceId,
|
||||
int QuestionCount,
|
||||
int? DurationMinutes,
|
||||
decimal? TotalScore,
|
||||
@@ -215,8 +210,8 @@ public sealed record PracticeSessionQuestionItem(
|
||||
public sealed record PracticeSessionReportItem(
|
||||
Guid Id,
|
||||
Guid PracticeSessionId,
|
||||
Guid? BlueprintId,
|
||||
Guid? CollectionId,
|
||||
AccessResourceType ResourceType,
|
||||
Guid ResourceId,
|
||||
string Mode,
|
||||
int TotalQuestions,
|
||||
int AnsweredCount,
|
||||
@@ -242,12 +237,8 @@ public sealed record PracticeSessionReportItem(
|
||||
public sealed record PracticeHistoryItem(
|
||||
Guid Id,
|
||||
string Mode,
|
||||
string? TargetType,
|
||||
Guid? TargetId,
|
||||
Guid? BlueprintId,
|
||||
Guid? CollectionId,
|
||||
Guid? EntryId,
|
||||
Guid? ContentNodeId,
|
||||
AccessResourceType ResourceType,
|
||||
Guid ResourceId,
|
||||
int QuestionCount,
|
||||
int AnsweredCount,
|
||||
int CorrectCount,
|
||||
|
||||
@@ -6,13 +6,9 @@ namespace Tiku.Domain.Learning;
|
||||
public sealed class PracticeSession : TenantEntity
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
public Guid? BlueprintId { get; set; }
|
||||
public Guid? CollectionId { get; set; }
|
||||
public Guid? EntryId { get; set; }
|
||||
public Guid? ContentNodeId { get; set; }
|
||||
public AccessResourceType ResourceType { get; set; }
|
||||
public Guid ResourceId { get; set; }
|
||||
public string Mode { get; set; } = "chapter";
|
||||
public string? TargetType { get; set; }
|
||||
public Guid? TargetId { get; set; }
|
||||
public DateTimeOffset StartedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
public DateTimeOffset? FinishedAt { get; set; }
|
||||
public int QuestionCount { get; set; }
|
||||
|
||||
@@ -95,8 +95,8 @@ public sealed class PracticeSessionReport : AuditableTenantEntity
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
public Guid PracticeSessionId { get; set; }
|
||||
public Guid? BlueprintId { get; set; }
|
||||
public Guid? CollectionId { get; set; }
|
||||
public AccessResourceType ResourceType { get; set; }
|
||||
public Guid ResourceId { get; set; }
|
||||
public string Mode { get; set; } = "mock_exam";
|
||||
public int TotalQuestions { get; set; }
|
||||
public int AnsweredCount { get; set; }
|
||||
|
||||
@@ -237,8 +237,8 @@ internal abstract partial class LearningActivityServiceBase
|
||||
TenantId = actor.TenantId,
|
||||
UserId = actor.UserId,
|
||||
PracticeSessionId = session.Id,
|
||||
BlueprintId = session.BlueprintId,
|
||||
CollectionId = session.CollectionId,
|
||||
ResourceType = session.ResourceType,
|
||||
ResourceId = session.ResourceId,
|
||||
Mode = session.Mode,
|
||||
TotalQuestions = totalQuestions,
|
||||
AnsweredCount = answeredCount,
|
||||
@@ -358,12 +358,8 @@ internal abstract partial class LearningActivityServiceBase
|
||||
return new PracticeSessionItem(
|
||||
item.Id,
|
||||
item.Mode,
|
||||
item.TargetType,
|
||||
item.TargetId,
|
||||
item.BlueprintId,
|
||||
item.CollectionId,
|
||||
item.EntryId,
|
||||
item.ContentNodeId,
|
||||
item.ResourceType,
|
||||
item.ResourceId,
|
||||
item.QuestionCount,
|
||||
item.DurationMinutes,
|
||||
item.TotalScore,
|
||||
@@ -389,8 +385,8 @@ internal abstract partial class LearningActivityServiceBase
|
||||
return new PracticeSessionReportItem(
|
||||
item.Id,
|
||||
item.PracticeSessionId,
|
||||
item.BlueprintId,
|
||||
item.CollectionId,
|
||||
item.ResourceType,
|
||||
item.ResourceId,
|
||||
item.Mode,
|
||||
item.TotalQuestions,
|
||||
item.AnsweredCount,
|
||||
|
||||
@@ -46,8 +46,6 @@ internal sealed class PracticeReportService(LearningServiceDependencies dependen
|
||||
report.TenantId == actor.TenantId &&
|
||||
report.UserId == actor.UserId);
|
||||
|
||||
if (filter.BlueprintId.HasValue) query = query.Where(report => report.BlueprintId == filter.BlueprintId.Value);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(filter.Mode)) query = query.Where(report => report.Mode == filter.Mode.Trim());
|
||||
|
||||
var items = await query
|
||||
@@ -85,12 +83,8 @@ internal sealed class PracticeReportService(LearningServiceDependencies dependen
|
||||
.Select(row => new PracticeHistoryItem(
|
||||
row.session.Id,
|
||||
row.session.Mode,
|
||||
row.session.TargetType,
|
||||
row.session.TargetId,
|
||||
row.session.BlueprintId,
|
||||
row.session.CollectionId,
|
||||
row.session.EntryId,
|
||||
row.session.ContentNodeId,
|
||||
row.session.ResourceType,
|
||||
row.session.ResourceId,
|
||||
row.session.QuestionCount,
|
||||
row.report?.AnsweredCount ?? 0,
|
||||
row.report?.CorrectCount ?? 0,
|
||||
|
||||
@@ -137,11 +137,8 @@ internal sealed class V2PracticeSessionService(LearningServiceDependencies depen
|
||||
TenantId = actor.TenantId,
|
||||
UserId = actor.UserId,
|
||||
Mode = mode,
|
||||
TargetType = resourceType.ToString(),
|
||||
TargetId = resourceId,
|
||||
BlueprintId = resourceType == AccessResourceType.BlueprintRelease ? resourceId : null,
|
||||
CollectionId = resourceType == AccessResourceType.CollectionRelease ? resourceId : null,
|
||||
ContentNodeId = resourceType == AccessResourceType.CurriculumNode ? resourceId : null,
|
||||
ResourceType = resourceType,
|
||||
ResourceId = resourceId,
|
||||
QuestionCount = distinctCandidates.Length,
|
||||
DurationMinutes = durationMinutes,
|
||||
TotalScore = totalScore,
|
||||
|
||||
@@ -14,7 +14,7 @@ internal sealed class PracticeSessionConfiguration : IEntityTypeConfiguration<Pr
|
||||
builder.ConfigureTenantEntity("practice_sessions");
|
||||
builder.HasAlternateKey(entity => new { entity.TenantId, entity.UserId, entity.Id });
|
||||
builder.Property(entity => entity.Mode).HasMaxLength(50);
|
||||
builder.Property(entity => entity.TargetType).HasMaxLength(50);
|
||||
builder.Property(entity => entity.ResourceType).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.StartedAt).HasDefaultValueSql("now()");
|
||||
builder.Property(entity => entity.TotalScore).HasPrecision(8, 2);
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum().HasDefaultValue(PracticeSessionStatus.Active);
|
||||
@@ -38,22 +38,6 @@ internal sealed class PracticeSessionConfiguration : IEntityTypeConfiguration<Pr
|
||||
builder.HasOne<User>().WithMany()
|
||||
.HasForeignKey(entity => entity.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<PracticeBlueprint>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.BlueprintId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<QuestionCollection>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.CollectionId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<ContentEntry>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.EntryId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<ContentNode>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.ContentNodeId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -185,6 +185,7 @@ internal sealed class PracticeSessionReportConfiguration : IEntityTypeConfigurat
|
||||
builder.ConfigureTenantEntity("practice_session_reports");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.Mode).HasMaxLength(50);
|
||||
builder.Property(entity => entity.ResourceType).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Score).HasPrecision(10, 2);
|
||||
builder.Property(entity => entity.TotalScore).HasPrecision(10, 2);
|
||||
builder.Property(entity => entity.Accuracy).HasPrecision(6, 4);
|
||||
@@ -199,8 +200,7 @@ internal sealed class PracticeSessionReportConfiguration : IEntityTypeConfigurat
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.PracticeSessionId }).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.SubmittedAt });
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.BlueprintId, entity.SubmittedAt })
|
||||
.HasFilter("blueprint_id is not null");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.ResourceType, entity.ResourceId, entity.SubmittedAt });
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_practice_session_reports_counts",
|
||||
@@ -217,14 +217,6 @@ internal sealed class PracticeSessionReportConfiguration : IEntityTypeConfigurat
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.PracticeSessionId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<PracticeBlueprint>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.BlueprintId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<QuestionCollection>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.CollectionId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,4 +291,4 @@ internal sealed class RevenueDailyStatConfiguration : IEntityTypeConfiguration<R
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26466
Tiku.Infrastructure/Persistence/Migrations/20260806021417_MigratePracticeSessionResourcesV2.Designer.cs
generated
Normal file
26466
Tiku.Infrastructure/Persistence/Migrations/20260806021417_MigratePracticeSessionResourcesV2.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,282 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MigratePracticeSessionResourcesV2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_practice_session_reports_practice_blueprints_tenant_id_blue~",
|
||||
table: "practice_session_reports");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_practice_session_reports_question_collections_tenant_id_col~",
|
||||
table: "practice_session_reports");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_practice_sessions_content_entries_tenant_id_entry_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_practice_sessions_content_nodes_tenant_id_content_node_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_practice_sessions_practice_blueprints_tenant_id_blueprint_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_practice_sessions_question_collections_tenant_id_collection~",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_practice_sessions_tenant_id_blueprint_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_practice_sessions_tenant_id_collection_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_practice_sessions_tenant_id_content_node_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_practice_sessions_tenant_id_entry_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_practice_session_reports_tenant_id_blueprint_id_submitted_at",
|
||||
table: "practice_session_reports");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_practice_session_reports_tenant_id_collection_id",
|
||||
table: "practice_session_reports");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "blueprint_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "collection_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "content_node_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "entry_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "target_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "target_type",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "blueprint_id",
|
||||
table: "practice_session_reports");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "collection_id",
|
||||
table: "practice_session_reports");
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "resource_id",
|
||||
table: "practice_sessions",
|
||||
type: "uuid",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "resource_type",
|
||||
table: "practice_sessions",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "resource_id",
|
||||
table: "practice_session_reports",
|
||||
type: "uuid",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "resource_type",
|
||||
table: "practice_session_reports",
|
||||
type: "character varying(32)",
|
||||
maxLength: 32,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_practice_session_reports_tenant_id_resource_type_resource_i~",
|
||||
table: "practice_session_reports",
|
||||
columns: new[] { "tenant_id", "resource_type", "resource_id", "submitted_at" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_practice_session_reports_tenant_id_resource_type_resource_i~",
|
||||
table: "practice_session_reports");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "resource_id",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "resource_type",
|
||||
table: "practice_sessions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "resource_id",
|
||||
table: "practice_session_reports");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "resource_type",
|
||||
table: "practice_session_reports");
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "blueprint_id",
|
||||
table: "practice_sessions",
|
||||
type: "uuid",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "collection_id",
|
||||
table: "practice_sessions",
|
||||
type: "uuid",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "content_node_id",
|
||||
table: "practice_sessions",
|
||||
type: "uuid",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "entry_id",
|
||||
table: "practice_sessions",
|
||||
type: "uuid",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "target_id",
|
||||
table: "practice_sessions",
|
||||
type: "uuid",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "target_type",
|
||||
table: "practice_sessions",
|
||||
type: "character varying(50)",
|
||||
maxLength: 50,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "blueprint_id",
|
||||
table: "practice_session_reports",
|
||||
type: "uuid",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "collection_id",
|
||||
table: "practice_session_reports",
|
||||
type: "uuid",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_practice_sessions_tenant_id_blueprint_id",
|
||||
table: "practice_sessions",
|
||||
columns: new[] { "tenant_id", "blueprint_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_practice_sessions_tenant_id_collection_id",
|
||||
table: "practice_sessions",
|
||||
columns: new[] { "tenant_id", "collection_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_practice_sessions_tenant_id_content_node_id",
|
||||
table: "practice_sessions",
|
||||
columns: new[] { "tenant_id", "content_node_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_practice_sessions_tenant_id_entry_id",
|
||||
table: "practice_sessions",
|
||||
columns: new[] { "tenant_id", "entry_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_practice_session_reports_tenant_id_blueprint_id_submitted_at",
|
||||
table: "practice_session_reports",
|
||||
columns: new[] { "tenant_id", "blueprint_id", "submitted_at" },
|
||||
filter: "blueprint_id is not null");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_practice_session_reports_tenant_id_collection_id",
|
||||
table: "practice_session_reports",
|
||||
columns: new[] { "tenant_id", "collection_id" });
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_practice_session_reports_practice_blueprints_tenant_id_blue~",
|
||||
table: "practice_session_reports",
|
||||
columns: new[] { "tenant_id", "blueprint_id" },
|
||||
principalTable: "practice_blueprints",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_practice_session_reports_question_collections_tenant_id_col~",
|
||||
table: "practice_session_reports",
|
||||
columns: new[] { "tenant_id", "collection_id" },
|
||||
principalTable: "question_collections",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_practice_sessions_content_entries_tenant_id_entry_id",
|
||||
table: "practice_sessions",
|
||||
columns: new[] { "tenant_id", "entry_id" },
|
||||
principalTable: "content_entries",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_practice_sessions_content_nodes_tenant_id_content_node_id",
|
||||
table: "practice_sessions",
|
||||
columns: new[] { "tenant_id", "content_node_id" },
|
||||
principalTable: "content_nodes",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_practice_sessions_practice_blueprints_tenant_id_blueprint_id",
|
||||
table: "practice_sessions",
|
||||
columns: new[] { "tenant_id", "blueprint_id" },
|
||||
principalTable: "practice_blueprints",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_practice_sessions_question_collections_tenant_id_collection~",
|
||||
table: "practice_sessions",
|
||||
columns: new[] { "tenant_id", "collection_id" },
|
||||
principalTable: "question_collections",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12281,30 +12281,14 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("authorization_expires_at");
|
||||
|
||||
b.Property<Guid?>("BlueprintId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("blueprint_id");
|
||||
|
||||
b.Property<Guid?>("CollectionId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("collection_id");
|
||||
|
||||
b.Property<int>("ConsumedFreeQuota")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("consumed_free_quota");
|
||||
|
||||
b.Property<Guid?>("ContentNodeId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("content_node_id");
|
||||
|
||||
b.Property<int?>("DurationMinutes")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("duration_minutes");
|
||||
|
||||
b.Property<Guid?>("EntryId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("entry_id");
|
||||
|
||||
b.Property<DateTimeOffset?>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expires_at");
|
||||
@@ -12333,6 +12317,16 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("question_count");
|
||||
|
||||
b.Property<Guid>("ResourceId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("resource_id");
|
||||
|
||||
b.Property<string>("ResourceType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("resource_type");
|
||||
|
||||
b.Property<DateTimeOffset>("StartedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
@@ -12351,15 +12345,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("strong_revocation_version");
|
||||
|
||||
b.Property<Guid?>("TargetId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("target_id");
|
||||
|
||||
b.Property<string>("TargetType")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("target_type");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
@@ -12392,18 +12377,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_practice_sessions_user_id");
|
||||
|
||||
b.HasIndex("TenantId", "BlueprintId")
|
||||
.HasDatabaseName("ix_practice_sessions_tenant_id_blueprint_id");
|
||||
|
||||
b.HasIndex("TenantId", "CollectionId")
|
||||
.HasDatabaseName("ix_practice_sessions_tenant_id_collection_id");
|
||||
|
||||
b.HasIndex("TenantId", "ContentNodeId")
|
||||
.HasDatabaseName("ix_practice_sessions_tenant_id_content_node_id");
|
||||
|
||||
b.HasIndex("TenantId", "EntryId")
|
||||
.HasDatabaseName("ix_practice_sessions_tenant_id_entry_id");
|
||||
|
||||
b.HasIndex("TenantId", "UserId", "StartedAt")
|
||||
.HasDatabaseName("ix_practice_sessions_tenant_id_user_id_started_at");
|
||||
|
||||
@@ -12555,14 +12528,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("answered_count");
|
||||
|
||||
b.Property<Guid?>("BlueprintId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("blueprint_id");
|
||||
|
||||
b.Property<Guid?>("CollectionId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("collection_id");
|
||||
|
||||
b.Property<int>("CorrectCount")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("correct_count");
|
||||
@@ -12609,6 +12574,16 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasColumnName("question_results")
|
||||
.HasDefaultValueSql("'[]'::jsonb");
|
||||
|
||||
b.Property<Guid>("ResourceId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("resource_id");
|
||||
|
||||
b.Property<string>("ResourceType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("resource_type");
|
||||
|
||||
b.Property<decimal>("Score")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("numeric(10,2)")
|
||||
@@ -12696,20 +12671,16 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_practice_session_reports_user_id");
|
||||
|
||||
b.HasIndex("TenantId", "CollectionId")
|
||||
.HasDatabaseName("ix_practice_session_reports_tenant_id_collection_id");
|
||||
|
||||
b.HasIndex("TenantId", "PracticeSessionId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_practice_session_reports_tenant_id_practice_session_id");
|
||||
|
||||
b.HasIndex("TenantId", "BlueprintId", "SubmittedAt")
|
||||
.HasDatabaseName("ix_practice_session_reports_tenant_id_blueprint_id_submitted_at")
|
||||
.HasFilter("blueprint_id is not null");
|
||||
|
||||
b.HasIndex("TenantId", "UserId", "SubmittedAt")
|
||||
.HasDatabaseName("ix_practice_session_reports_tenant_id_user_id_submitted_at");
|
||||
|
||||
b.HasIndex("TenantId", "ResourceType", "ResourceId", "SubmittedAt")
|
||||
.HasDatabaseName("ix_practice_session_reports_tenant_id_resource_type_resource_i~");
|
||||
|
||||
b.ToTable("practice_session_reports", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_practice_session_reports_accuracy", "accuracy >= 0 and accuracy <= 1");
|
||||
@@ -24557,34 +24528,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_practice_sessions_users_user_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Content.PracticeBlueprint", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "BlueprintId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("fk_practice_sessions_practice_blueprints_tenant_id_blueprint_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Content.QuestionCollection", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "CollectionId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("fk_practice_sessions_question_collections_tenant_id_collection~");
|
||||
|
||||
b.HasOne("Tiku.Domain.Content.ContentNode", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "ContentNodeId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("fk_practice_sessions_content_nodes_tenant_id_content_node_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Content.ContentEntry", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "EntryId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("fk_practice_sessions_content_entries_tenant_id_entry_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionQuestion", b =>
|
||||
@@ -24661,20 +24604,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_practice_session_reports_users_user_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Content.PracticeBlueprint", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "BlueprintId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("fk_practice_session_reports_practice_blueprints_tenant_id_blue~");
|
||||
|
||||
b.HasOne("Tiku.Domain.Content.QuestionCollection", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "CollectionId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("fk_practice_session_reports_question_collections_tenant_id_col~");
|
||||
|
||||
b.HasOne("Tiku.Domain.Learning.PracticeSession", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "PracticeSessionId")
|
||||
|
||||
@@ -43,7 +43,7 @@ public sealed class LearningEndpointTests
|
||||
using var scope = factory.CreateSystemScope("Verify atomic V2 practice quota");
|
||||
var db = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
||||
Assert.Equal(1, await db.PracticeSessions.CountAsync(item =>
|
||||
item.TenantId == seed.TenantId && item.UserId == seed.UserId && item.TargetId == releaseId));
|
||||
item.TenantId == seed.TenantId && item.UserId == seed.UserId && item.ResourceId == releaseId));
|
||||
Assert.Equal(1, await db.StudentEntitlementsV2
|
||||
.Where(item => item.TenantId == seed.TenantId && item.UserId == seed.UserId)
|
||||
.Select(item => item.UsedQuestionCount)
|
||||
|
||||
Reference in New Issue
Block a user