diff --git a/README.md b/README.md index 15890cf..b37629c 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,8 @@ Tiku.IntegrationTests # API / EF 模型集成测试 - ASP.NET Authentication 负责身份认证。 - ASP.NET Authorization 负责权限策略。 - JWT + 数据库 `auth_sessions` 负责 access/refresh/session 闭环。 -- `ICurrentUser` / `ICurrentTenant` 统一当前用户和租户上下文。 +- `ICurrentUser` / 只读 `ITenantContext` 统一当前用户和请求租户上下文。 +- EF Core Query Filter、写入拦截器和 PostgreSQL 组合约束共同阻断跨租户读写。 - PostgreSQL FK / unique / check / index 负责数据完整性底线。 - 审计事件表记录关键行为。 @@ -64,7 +65,7 @@ Tiku.IntegrationTests # API / EF 模型集成测试 旧版对连接池、日志、跨域、限流、配置校验这些工程底座比较薄。新后端已经补上: -- 单例 `NpgsqlDataSource` + `AddDbContextPool`。 +- 单例 `NpgsqlDataSource` + scoped `AddDbContext`,避免租户状态跨请求复用。 - Serilog 结构化日志和请求上下文日志。 - 集中 CORS 配置,生产默认不放开 Origin。 - ASP.NET RateLimiter 全局限流。 @@ -89,18 +90,19 @@ Tiku.IntegrationTests # API / EF 模型集成测试 数据库模型迁移已经完成到 greenfield 初始 schema: ```text -Tiku.Infrastructure/Persistence/Migrations/20260725220742_InitialSchema.cs +Tiku.Infrastructure/Persistence/Migrations/20260727084726_InitialSchema.cs ``` 当前 EF 模型覆盖: - 租户、用户、成员、认证、Session、短信验证码 - 地区、模块、院校、专业、科目、分类 -- 题库、题目、题目版本 -- 内容入口、内容节点、题集、练习蓝图 +- 平台公共题库、租户私有题库、题目引用和题目版本 +- 公共分类主干、租户扩展分类、内容入口、题集和练习蓝图 - 词汇、手册、用户单词进度 - 资源、图片、App 资源、视频解析、导入任务 -- 练习、答题、收藏、错题、报告、统计 +- 版本锁定练习、答题、收藏、错题、报告、统计 +- 自定义域名 DNS/TLS 生命周期和版本化前端运行时配置 - 商品、订单、支付、权益、兑换码、优惠券 - 推广、CRM、佣金 - Banner、FAQ、公告、通知、徽章、审计 diff --git a/Tiku.Api/Contracts/AuthDtos.cs b/Tiku.Api/Contracts/AuthDtos.cs index 8bc1ae8..32f1d4e 100644 --- a/Tiku.Api/Contracts/AuthDtos.cs +++ b/Tiku.Api/Contracts/AuthDtos.cs @@ -10,11 +10,11 @@ namespace Tiku.Api.Contracts; public sealed class PasswordLoginDto { /// - /// 租户 ID。登录阶段允许客户端指定租户,登录后的业务接口以 JWT/session 解析出的当前租户为准。 + /// 平台控制域名登录时使用的租户代码;自定义域名登录可省略。 /// - [Required] - [Description("租户 ID。登录阶段允许客户端指定租户,登录后的业务接口以 JWT/session 解析出的当前租户为准。")] - public Guid TenantId { get; set; } + [StringLength(100)] + [Description("平台控制域名登录时使用的租户代码;自定义域名登录可省略。")] + public string? TenantCode { get; set; } /// /// 手机号,建议前端提交规范化后的中国大陆手机号。 @@ -39,11 +39,11 @@ public sealed class PasswordLoginDto public sealed class SmsLoginDto { /// - /// 租户 ID。 + /// 平台控制域名登录时使用的租户代码;自定义域名登录可省略。 /// - [Required] - [Description("租户 ID。")] - public Guid TenantId { get; set; } + [StringLength(100)] + [Description("平台控制域名登录时使用的租户代码;自定义域名登录可省略。")] + public string? TenantCode { get; set; } /// /// 中国大陆手机号。 @@ -68,11 +68,11 @@ public sealed class SmsLoginDto public sealed class OAuthCodeDto { /// - /// 租户 ID。 + /// 平台控制域名登录时使用的租户代码;自定义域名登录可省略。 /// - [Required] - [Description("租户 ID。")] - public Guid TenantId { get; set; } + [StringLength(100)] + [Description("平台控制域名登录时使用的租户代码;自定义域名登录可省略。")] + public string? TenantCode { get; set; } /// /// OAuth 平台返回的一次性授权 code。 diff --git a/Tiku.Api/Contracts/ContentManagementDtos.cs b/Tiku.Api/Contracts/ContentManagementDtos.cs index 4e3be02..60a0c45 100644 --- a/Tiku.Api/Contracts/ContentManagementDtos.cs +++ b/Tiku.Api/Contracts/ContentManagementDtos.cs @@ -1,7 +1,9 @@ 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; @@ -252,6 +254,9 @@ public sealed class CollectionQuestionDto [Required] public Guid QuestionId { get; set; } + [Required] + public QuestionSource Source { get; set; } = QuestionSource.Tenant; + [StringLength(100)] public string? SectionKey { get; set; } @@ -265,7 +270,13 @@ public sealed class CollectionQuestionDto public CollectionQuestionCommand ToCommand() { - return new CollectionQuestionCommand(QuestionId, SectionKey, Order, Score, Required, Metadata); + return new CollectionQuestionCommand( + new QuestionLocator(Source, QuestionId), + SectionKey, + Order, + Score, + Required, + Metadata); } } diff --git a/Tiku.Api/Contracts/LearningDtos.cs b/Tiku.Api/Contracts/LearningDtos.cs index 8e293cd..9c86f46 100644 --- a/Tiku.Api/Contracts/LearningDtos.cs +++ b/Tiku.Api/Contracts/LearningDtos.cs @@ -1,7 +1,9 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json; using Tiku.Application.Learning; +using Tiku.Application.QuestionBanks; using Tiku.Domain.Common; +using Tiku.Domain.Content; namespace Tiku.Api.Contracts; @@ -107,9 +109,7 @@ public sealed class SubmitPracticeSessionDto public sealed class SubmitAnswerDto { [Required] - public Guid QuestionId { get; set; } - - public Guid? PracticeSessionId { get; set; } + public Guid SessionQuestionId { get; set; } public IReadOnlyCollection? SelectedOptions { get; set; } @@ -121,8 +121,7 @@ public sealed class SubmitAnswerDto public SubmitAnswerCommand ToCommand() { return new SubmitAnswerCommand( - QuestionId, - PracticeSessionId, + SessionQuestionId, SelectedOptions, AnswerText, SelfJudgedCorrect); @@ -134,11 +133,14 @@ public sealed class QuestionActionDto [Required] public Guid QuestionId { get; set; } + [Required] + public QuestionSource Source { get; set; } = QuestionSource.Tenant; + public bool? Favorite { get; set; } public QuestionActionCommand ToCommand() { - return new QuestionActionCommand(QuestionId, Favorite); + return new QuestionActionCommand(new QuestionLocator(Source, QuestionId), Favorite); } } diff --git a/Tiku.Api/Contracts/QuestionBankDtos.cs b/Tiku.Api/Contracts/QuestionBankDtos.cs index a319a51..e5a4486 100644 --- a/Tiku.Api/Contracts/QuestionBankDtos.cs +++ b/Tiku.Api/Contracts/QuestionBankDtos.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using Tiku.Application.QuestionBanks; +using Tiku.Domain.Content; namespace Tiku.Api.Contracts; @@ -24,6 +25,8 @@ public sealed class QuestionBankQueryDto public Guid? CollectionId { get; set; } + public QuestionSource? Source { get; set; } + [StringLength(50)] public string? Type { get; set; } @@ -54,7 +57,8 @@ public sealed class QuestionBankQueryDto ParseQuestionIds(), Type, Keyword, - Limit); + Limit, + Source); } private Guid[] ParseQuestionIds() diff --git a/Tiku.Api/Contracts/TaxonomyDtos.cs b/Tiku.Api/Contracts/TaxonomyDtos.cs new file mode 100644 index 0000000..0d2dbef --- /dev/null +++ b/Tiku.Api/Contracts/TaxonomyDtos.cs @@ -0,0 +1,31 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json; +using Tiku.Application.Catalog; +using Tiku.Domain.Catalog; +using Tiku.Domain.Common; +using Tiku.Domain.Content; + +namespace Tiku.Api.Contracts; + +public sealed class CreateTaxonomyNodeDto +{ + public Guid? ParentId { get; set; } + public QuestionSource? ParentSource { get; set; } + [Required] + public TaxonomyNodeType NodeType { get; set; } + [Required, StringLength(100)] + public string Code { get; set; } = string.Empty; + [Required, StringLength(300)] + public string Name { get; set; } = string.Empty; + public int SortOrder { get; set; } + public JsonElement Metadata { get; set; } = JsonDefaults.Object(); + + public CreateTaxonomyNodeCommand ToCommand() => new( + ParentId, + ParentSource, + NodeType, + Code, + Name, + SortOrder, + Metadata); +} diff --git a/Tiku.Api/Contracts/TenantFrontendConfigDtos.cs b/Tiku.Api/Contracts/TenantFrontendConfigDtos.cs new file mode 100644 index 0000000..d97da42 --- /dev/null +++ b/Tiku.Api/Contracts/TenantFrontendConfigDtos.cs @@ -0,0 +1,28 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json; +using Tiku.Application.Tenancy; +using Tiku.Domain.Common; + +namespace Tiku.Api.Contracts; + +public sealed class SaveTenantFrontendConfigDraftDto +{ + public JsonElement Branding { get; set; } = JsonDefaults.Object(); + public JsonElement Theme { get; set; } = JsonDefaults.Object(); + public JsonElement Features { get; set; } = JsonDefaults.Object(); + public JsonElement Navigation { get; set; } = JsonDefaults.Array(); + public JsonElement HomeModules { get; set; } = JsonDefaults.Array(); + + public TenantFrontendConfigDraft ToDraft() => new( + Branding, + Theme, + Features, + Navigation, + HomeModules); +} + +public sealed class PublishTenantFrontendConfigDto +{ + [Range(1, int.MaxValue)] + public int ExpectedVersion { get; set; } +} diff --git a/Tiku.Api/Controllers/AssetsController.cs b/Tiku.Api/Controllers/AssetsController.cs index a672627..f281f4a 100644 --- a/Tiku.Api/Controllers/AssetsController.cs +++ b/Tiku.Api/Controllers/AssetsController.cs @@ -15,7 +15,7 @@ namespace Tiku.Api.Controllers; [Route("api/assets")] public sealed class AssetsController( IAssetAccessService assetAccessService, - ICurrentTenant currentTenant, + ITenantContext currentTenant, ICurrentUser currentUser, TikuDbContext dbContext) : ControllerBase { diff --git a/Tiku.Api/Controllers/AuthController.cs b/Tiku.Api/Controllers/AuthController.cs index ea15182..fd158c5 100644 --- a/Tiku.Api/Controllers/AuthController.cs +++ b/Tiku.Api/Controllers/AuthController.cs @@ -2,13 +2,21 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tiku.Application.Auth; using Tiku.Api.Contracts; +using Tiku.Application.Security; +using Tiku.Application.Tenancy; +using Tiku.Infrastructure.Content; namespace Tiku.Api.Controllers; [ApiController] [Route("api/auth")] [Produces("application/json")] -public sealed class AuthController(IAuthService authService) : ControllerBase +public sealed class AuthController( + IAuthService authService, + ISessionService sessionService, + ITenantContext tenantContext, + ITenantContextInitializer tenantContextInitializer, + ITenantDirectory tenantDirectory) : ControllerBase { [AllowAnonymous] [HttpPost("login/password")] @@ -22,7 +30,7 @@ public sealed class AuthController(IAuthService authService) : ControllerBase { var result = await authService.LoginWithPasswordAsync( new PasswordLoginRequest( - request.TenantId, + await ResolveTenantIdAsync(request.TenantCode, cancellationToken), request.Phone, request.Password, GetIpAddress(), @@ -44,7 +52,7 @@ public sealed class AuthController(IAuthService authService) : ControllerBase { var result = await authService.LoginWithSmsAsync( new SmsLoginRequest( - request.TenantId, + await ResolveTenantIdAsync(request.TenantCode, cancellationToken), request.Phone, request.Code, GetIpAddress(), @@ -67,7 +75,7 @@ public sealed class AuthController(IAuthService authService) : ControllerBase { var result = await authService.LoginWithWechatWebAsync( new WechatLoginRequest( - request.TenantId, + await ResolveTenantIdAsync(request.TenantCode, cancellationToken), request.Code, GetIpAddress(), Request.Headers.UserAgent.ToString()), @@ -89,7 +97,7 @@ public sealed class AuthController(IAuthService authService) : ControllerBase { var result = await authService.LoginWithWechatMiniAppAsync( new WechatLoginRequest( - request.TenantId, + await ResolveTenantIdAsync(request.TenantCode, cancellationToken), request.Code, GetIpAddress(), Request.Headers.UserAgent.ToString()), @@ -106,6 +114,7 @@ public sealed class AuthController(IAuthService authService) : ControllerBase [FromBody] RefreshSessionDto request, CancellationToken cancellationToken) { + ResolveRefreshTokenTenant(request.RefreshToken); var result = await authService.RefreshAsync( new RefreshSessionRequest( request.RefreshToken, @@ -125,6 +134,7 @@ public sealed class AuthController(IAuthService authService) : ControllerBase [FromBody] RefreshSessionDto request, CancellationToken cancellationToken) { + ResolveRefreshTokenTenant(request.RefreshToken); await authService.LogoutAsync( new LogoutSessionRequest(request.RefreshToken), cancellationToken); @@ -132,8 +142,51 @@ public sealed class AuthController(IAuthService authService) : ControllerBase return NoContent(); } + private void ResolveRefreshTokenTenant(string refreshToken) + { + if (!sessionService.TryParseRefreshToken(refreshToken, out var locator)) + { + return; + } + + tenantContextInitializer.Initialize(locator.TenantId, null, TenantResolutionSource.RefreshToken); + } + private string? GetIpAddress() { return HttpContext.Connection.RemoteIpAddress?.ToString(); } + + private async Task ResolveTenantIdAsync(string? tenantCode, CancellationToken cancellationToken) + { + if (tenantContext.TenantId.HasValue) + { + if (!string.IsNullOrWhiteSpace(tenantCode) && + !string.Equals(tenantContext.TenantCode, tenantCode.Trim(), StringComparison.OrdinalIgnoreCase)) + { + var supplied = await tenantDirectory.FindByCodeAsync(tenantCode.Trim(), cancellationToken); + if (supplied?.TenantId != tenantContext.TenantId.Value) + { + throw new TenantContextConflictException( + tenantContext.TenantId.Value, + supplied?.TenantId ?? Guid.Empty); + } + } + + return tenantContext.TenantId.Value; + } + + if (string.IsNullOrWhiteSpace(tenantCode)) + { + throw new RequiredFieldException("tenantCode is required when the request host does not resolve a tenant."); + } + + var tenant = await tenantDirectory.FindByCodeAsync(tenantCode.Trim(), cancellationToken) + ?? throw new TenantNotFoundException(); + tenantContextInitializer.Initialize( + tenant.TenantId, + tenant.TenantCode, + TenantResolutionSource.TenantCode); + return tenant.TenantId; + } } diff --git a/Tiku.Api/Controllers/CatalogController.cs b/Tiku.Api/Controllers/CatalogController.cs index 6033a38..822c0ab 100644 --- a/Tiku.Api/Controllers/CatalogController.cs +++ b/Tiku.Api/Controllers/CatalogController.cs @@ -23,7 +23,7 @@ public sealed class CatalogController( IQuestionBankQueryService questionBankQueryService, IStudyContentQueryService studyContentQueryService, IAssetQueryService assetQueryService, - ICurrentTenant currentTenant, + ITenantContext currentTenant, TikuDbContext dbContext) : ControllerBase { [HttpGet("regions")] diff --git a/Tiku.Api/Controllers/CommerceController.cs b/Tiku.Api/Controllers/CommerceController.cs index 466179a..aafc9d1 100644 --- a/Tiku.Api/Controllers/CommerceController.cs +++ b/Tiku.Api/Controllers/CommerceController.cs @@ -6,6 +6,7 @@ using System.Text.Json; using Tiku.Api.Contracts; using Tiku.Application.Commerce; using Tiku.Application.Security; +using Tiku.Application.Tenancy; namespace Tiku.Api.Controllers; @@ -16,7 +17,9 @@ namespace Tiku.Api.Controllers; public sealed class CommerceController( ICommerceService commerceService, ICurrentUser currentUser, - ICurrentTenant currentTenant) : ControllerBase + ITenantContext currentTenant, + ITenantContextInitializer tenantInitializer, + ITenantDirectory tenantDirectory) : ControllerBase { [HttpPost("orders")] [EndpointSummary("创建学生端订单")] @@ -125,10 +128,13 @@ public sealed class CommerceController( [EndpointSummary("微信支付回调")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> WechatPayNotify( - [FromQuery] Guid tenantId, + [FromQuery] string? tenantCode, CancellationToken cancellationToken) { - return Ok(await ProcessNotificationAsync(tenantId, PaymentProviders.WechatPay, cancellationToken)); + return Ok(await ProcessNotificationAsync( + await ResolveNotificationTenantAsync(tenantCode, cancellationToken), + PaymentProviders.WechatPay, + cancellationToken)); } [AllowAnonymous] @@ -136,10 +142,33 @@ public sealed class CommerceController( [EndpointSummary("支付宝支付回调")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> AlipayNotify( - [FromQuery] Guid tenantId, + [FromQuery] string? tenantCode, CancellationToken cancellationToken) { - return Ok(await ProcessNotificationAsync(tenantId, PaymentProviders.Alipay, cancellationToken)); + return Ok(await ProcessNotificationAsync( + await ResolveNotificationTenantAsync(tenantCode, cancellationToken), + PaymentProviders.Alipay, + cancellationToken)); + } + + private async Task ResolveNotificationTenantAsync( + string? tenantCode, + CancellationToken cancellationToken) + { + if (currentTenant.TenantId.HasValue) + { + return currentTenant.TenantId.Value; + } + + if (string.IsNullOrWhiteSpace(tenantCode)) + { + throw new CommerceException("Tenant code is required for payment notification.", "tenant_required"); + } + + var tenant = await tenantDirectory.FindByCodeAsync(tenantCode.Trim(), cancellationToken) + ?? throw new CommerceException("Tenant was not found.", "tenant_not_found"); + tenantInitializer.Initialize(tenant.TenantId, tenant.TenantCode, TenantResolutionSource.TenantCode); + return tenant.TenantId; } private CommerceActor ResolveActor() diff --git a/Tiku.Api/Controllers/CommissionController.cs b/Tiku.Api/Controllers/CommissionController.cs index e3020ba..1dea7c9 100644 --- a/Tiku.Api/Controllers/CommissionController.cs +++ b/Tiku.Api/Controllers/CommissionController.cs @@ -13,7 +13,7 @@ namespace Tiku.Api.Controllers; public sealed class CommissionController( ICommissionService commissionService, ICurrentUser currentUser, - ICurrentTenant currentTenant) : ControllerBase + ITenantContext currentTenant) : ControllerBase { [HttpGet("settings")] public async Task> Settings(CancellationToken cancellationToken) => diff --git a/Tiku.Api/Controllers/CrmController.cs b/Tiku.Api/Controllers/CrmController.cs index 179822e..e8bca9c 100644 --- a/Tiku.Api/Controllers/CrmController.cs +++ b/Tiku.Api/Controllers/CrmController.cs @@ -13,7 +13,7 @@ namespace Tiku.Api.Controllers; public sealed class CrmController( ICrmService crmService, ICurrentUser currentUser, - ICurrentTenant currentTenant) : ControllerBase + ITenantContext currentTenant) : ControllerBase { [HttpGet("config")] [EndpointSummary("查询 CRM 推送配置")] diff --git a/Tiku.Api/Controllers/LearningController.cs b/Tiku.Api/Controllers/LearningController.cs index 05e096f..45be92b 100644 --- a/Tiku.Api/Controllers/LearningController.cs +++ b/Tiku.Api/Controllers/LearningController.cs @@ -13,7 +13,7 @@ namespace Tiku.Api.Controllers; public sealed class LearningController( ILearningActivityService learningActivityService, ICurrentUser currentUser, - ICurrentTenant currentTenant) : ControllerBase + ITenantContext currentTenant) : ControllerBase { [HttpGet("stats")] [EndpointSummary("查询学习统计")] diff --git a/Tiku.Api/Controllers/PointsController.cs b/Tiku.Api/Controllers/PointsController.cs index 3577d6a..e4144b5 100644 --- a/Tiku.Api/Controllers/PointsController.cs +++ b/Tiku.Api/Controllers/PointsController.cs @@ -13,7 +13,7 @@ namespace Tiku.Api.Controllers; public sealed class PointsController( IPointService pointService, ICurrentUser currentUser, - ICurrentTenant currentTenant) : ControllerBase + ITenantContext currentTenant) : ControllerBase { [HttpGet("summary")] [EndpointSummary("查询当前用户积分摘要")] diff --git a/Tiku.Api/Controllers/ProfileController.cs b/Tiku.Api/Controllers/ProfileController.cs index 6dae31e..3d9d82e 100644 --- a/Tiku.Api/Controllers/ProfileController.cs +++ b/Tiku.Api/Controllers/ProfileController.cs @@ -14,7 +14,7 @@ namespace Tiku.Api.Controllers; public sealed class ProfileController( IProfileService profileService, ICurrentUser currentUser, - ICurrentTenant currentTenant) : ControllerBase + ITenantContext currentTenant) : ControllerBase { [HttpGet("me")] [EndpointSummary("获取当前学生资料")] diff --git a/Tiku.Api/Controllers/ReferralController.cs b/Tiku.Api/Controllers/ReferralController.cs index 30db0fb..316642a 100644 --- a/Tiku.Api/Controllers/ReferralController.cs +++ b/Tiku.Api/Controllers/ReferralController.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore; using Tiku.Api.Contracts; using Tiku.Application.Growth; using Tiku.Application.Security; +using Tiku.Application.Tenancy; using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Persistence; @@ -15,8 +16,9 @@ namespace Tiku.Api.Controllers; public sealed class ReferralController( IReferralService referralService, ICurrentUser currentUser, - ICurrentTenant currentTenant, - TikuDbContext dbContext) : ControllerBase + ITenantContext currentTenant, + ITenantContextInitializer tenantInitializer, + ITenantDirectory tenantDirectory) : ControllerBase { [HttpPost("invite-code")] [Authorize(Policy = TikuPolicies.CurrentTenantMember)] @@ -223,13 +225,9 @@ public sealed class ReferralController( throw new TenantNotFoundException(); } - var tenantId = await dbContext.Tenants - .Where(tenant => - tenant.Slug == resolvedTenantCode.Trim() && - tenant.Status == TenantStatus.Active) - .Select(tenant => (Guid?)tenant.Id) - .SingleOrDefaultAsync(cancellationToken); - - return tenantId ?? throw new TenantNotFoundException(); + var tenant = await tenantDirectory.FindByCodeAsync(resolvedTenantCode.Trim(), cancellationToken) + ?? throw new TenantNotFoundException(); + tenantInitializer.Initialize(tenant.TenantId, tenant.TenantCode, TenantResolutionSource.TenantCode); + return tenant.TenantId; } } diff --git a/Tiku.Api/Controllers/RuntimeController.cs b/Tiku.Api/Controllers/RuntimeController.cs new file mode 100644 index 0000000..005c24a --- /dev/null +++ b/Tiku.Api/Controllers/RuntimeController.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Tiku.Application.Security; +using Tiku.Application.Tenancy; + +namespace Tiku.Api.Controllers; + +[ApiController] +[AllowAnonymous] +[Produces("application/json")] +[Route("api/runtime")] +public sealed class RuntimeController( + ITenantContext tenantContext, + ITenantFrontendConfigService frontendConfigService) : ControllerBase +{ + [HttpGet("bootstrap")] + [EndpointSummary("获取租户前端运行时配置")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status304NotModified)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task> Bootstrap(CancellationToken cancellationToken) + { + if (!tenantContext.TenantId.HasValue) + { + return NotFound(new ProblemDetails + { + Title = "Tenant was not found.", + Status = StatusCodes.Status404NotFound + }); + } + + var runtime = await frontendConfigService.GetRuntimeAsync( + tenantContext.TenantId.Value, + cancellationToken); + var etag = $"\"{runtime.TenantCode}-{runtime.ConfigVersion}\""; + if (Request.Headers.IfNoneMatch.Any(value => string.Equals(value, etag, StringComparison.Ordinal))) + { + return StatusCode(StatusCodes.Status304NotModified); + } + + Response.Headers.ETag = etag; + Response.Headers.CacheControl = "public,max-age=60,must-revalidate"; + return Ok(runtime); + } +} diff --git a/Tiku.Api/Controllers/ScorelineController.cs b/Tiku.Api/Controllers/ScorelineController.cs index 3d753a8..8781d62 100644 --- a/Tiku.Api/Controllers/ScorelineController.cs +++ b/Tiku.Api/Controllers/ScorelineController.cs @@ -16,7 +16,7 @@ namespace Tiku.Api.Controllers; [Route("api/scoreline")] public sealed class ScorelineController( IScorelineQueryService scorelineQueryService, - ICurrentTenant currentTenant, + ITenantContext currentTenant, TikuDbContext dbContext) : ControllerBase { private static readonly string[] DynamicPrefixes = ["field.", "min.", "max."]; diff --git a/Tiku.Api/Controllers/SecurityDiagnosticsController.cs b/Tiku.Api/Controllers/SecurityDiagnosticsController.cs index 5bca13f..9c32f1e 100644 --- a/Tiku.Api/Controllers/SecurityDiagnosticsController.cs +++ b/Tiku.Api/Controllers/SecurityDiagnosticsController.cs @@ -9,7 +9,7 @@ namespace Tiku.Api.Controllers; [Route("api/_security")] public sealed class SecurityDiagnosticsController( ICurrentUser currentUser, - ICurrentTenant currentTenant) : ControllerBase + ITenantContext currentTenant) : ControllerBase { [Authorize(Policy = TikuPolicies.AuthenticatedUser)] [HttpGet("authenticated")] @@ -29,7 +29,7 @@ public sealed class SecurityDiagnosticsController( return Ok(new { currentTenant.TenantId, - currentTenant.Role + currentUser.TenantRole }); } @@ -40,7 +40,7 @@ public sealed class SecurityDiagnosticsController( return Ok(new { currentTenant.TenantId, - currentTenant.Role + currentUser.TenantRole }); } } diff --git a/Tiku.Api/Controllers/TaxonomyController.cs b/Tiku.Api/Controllers/TaxonomyController.cs new file mode 100644 index 0000000..a7206d5 --- /dev/null +++ b/Tiku.Api/Controllers/TaxonomyController.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Tiku.Api.Contracts; +using Tiku.Application.Catalog; +using Tiku.Application.Security; + +namespace Tiku.Api.Controllers; + +[ApiController] +[Authorize(Policy = TikuPolicies.CurrentTenantMember)] +[Produces("application/json")] +[Route("api/taxonomy/nodes")] +public sealed class TaxonomyController( + ITenantContext tenantContext, + ITaxonomyService taxonomyService) : ControllerBase +{ + [HttpGet] + public Task> List(CancellationToken cancellationToken) + { + return taxonomyService.ListAsync(RequireTenantId(), cancellationToken); + } + + [HttpPost] + [Authorize(Policy = TikuPolicies.TenantAdmin)] + public Task Create( + CreateTaxonomyNodeDto request, + CancellationToken cancellationToken) + { + return taxonomyService.CreateAsync(RequireTenantId(), request.ToCommand(), cancellationToken); + } + + private Guid RequireTenantId() + { + return tenantContext.TenantId ?? throw new InvalidOperationException("Tenant was not resolved."); + } +} diff --git a/Tiku.Api/Controllers/TenantAdminDirectController.cs b/Tiku.Api/Controllers/TenantAdminDirectController.cs index df0e6ce..f702732 100644 --- a/Tiku.Api/Controllers/TenantAdminDirectController.cs +++ b/Tiku.Api/Controllers/TenantAdminDirectController.cs @@ -16,7 +16,7 @@ namespace Tiku.Api.Controllers; public sealed class TenantAdminDirectController( ITenantAdminDirectService tenantAdminService, ICurrentUser currentUser, - ICurrentTenant currentTenant) : ControllerBase + ITenantContext currentTenant) : ControllerBase { [HttpGet("classes")] [EndpointSummary("查询租户班级")] @@ -406,7 +406,7 @@ public sealed class TenantAdminDirectController( } var role = Enum.TryParse( - currentTenant.Role?.Replace("_", string.Empty, StringComparison.Ordinal), + currentUser.TenantRole?.Replace("_", string.Empty, StringComparison.Ordinal), ignoreCase: true, out var parsedRole) ? parsedRole diff --git a/Tiku.Api/Controllers/TenantCommerceController.cs b/Tiku.Api/Controllers/TenantCommerceController.cs index c76d960..8a2053f 100644 --- a/Tiku.Api/Controllers/TenantCommerceController.cs +++ b/Tiku.Api/Controllers/TenantCommerceController.cs @@ -13,7 +13,7 @@ namespace Tiku.Api.Controllers; public sealed class TenantCommerceController( ICommerceAdminService commerceAdminService, ICurrentUser currentUser, - ICurrentTenant currentTenant) : ControllerBase + ITenantContext currentTenant) : ControllerBase { [HttpGet("payment-accounts")] [EndpointSummary("查询租户支付账号")] diff --git a/Tiku.Api/Controllers/TenantContentController.cs b/Tiku.Api/Controllers/TenantContentController.cs index dff23c5..eff7f03 100644 --- a/Tiku.Api/Controllers/TenantContentController.cs +++ b/Tiku.Api/Controllers/TenantContentController.cs @@ -16,7 +16,7 @@ public sealed class TenantContentController( IAssetManagementService assetManagementService, IContentManagementService contentManagementService, ICurrentUser currentUser, - ICurrentTenant currentTenant) : ControllerBase + ITenantContext currentTenant) : ControllerBase { [HttpGet("entries")] [EndpointSummary("查询租户内容入口")] diff --git a/Tiku.Api/Controllers/TenantContentDirectController.cs b/Tiku.Api/Controllers/TenantContentDirectController.cs index 94cffb7..c2fdb5c 100644 --- a/Tiku.Api/Controllers/TenantContentDirectController.cs +++ b/Tiku.Api/Controllers/TenantContentDirectController.cs @@ -17,7 +17,7 @@ namespace Tiku.Api.Controllers; public sealed class TenantContentDirectController( IDirectContentService directContentService, ICurrentUser currentUser, - ICurrentTenant currentTenant) : ControllerBase + ITenantContext currentTenant) : ControllerBase { [HttpPost("questions")] [EndpointSummary("创建题目及首个版本")] diff --git a/Tiku.Api/Controllers/TenantFrontendConfigController.cs b/Tiku.Api/Controllers/TenantFrontendConfigController.cs new file mode 100644 index 0000000..79cd288 --- /dev/null +++ b/Tiku.Api/Controllers/TenantFrontendConfigController.cs @@ -0,0 +1,51 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Tiku.Api.Contracts; +using Tiku.Application.Security; +using Tiku.Application.Tenancy; + +namespace Tiku.Api.Controllers; + +[ApiController] +[Authorize(Policy = TikuPolicies.TenantAdmin)] +[Produces("application/json")] +[Route("api/tenant-admin/frontend-config")] +public sealed class TenantFrontendConfigController( + ITenantContext tenantContext, + ITenantFrontendConfigService frontendConfigService) : ControllerBase +{ + [HttpGet] + public Task Get(CancellationToken cancellationToken) + { + return frontendConfigService.GetAsync(RequireTenantId(), cancellationToken); + } + + [HttpPut("draft")] + public Task SaveDraft( + SaveTenantFrontendConfigDraftDto request, + CancellationToken cancellationToken) + { + return frontendConfigService.SaveDraftAsync( + RequireTenantId(), + request.ToDraft(), + cancellationToken); + } + + [HttpPost("publish")] + public Task Publish( + PublishTenantFrontendConfigDto request, + CancellationToken cancellationToken) + { + return frontendConfigService.PublishAsync( + RequireTenantId(), + request.ExpectedVersion, + cancellationToken); + } + + private Guid RequireTenantId() + { + return tenantContext.TenantId ?? throw new TenantFrontendConfigException( + "tenant_not_resolved", + "Tenant was not resolved."); + } +} diff --git a/Tiku.Api/Controllers/TenantPublicController.cs b/Tiku.Api/Controllers/TenantPublicController.cs index cdae94b..4d702cd 100644 --- a/Tiku.Api/Controllers/TenantPublicController.cs +++ b/Tiku.Api/Controllers/TenantPublicController.cs @@ -7,6 +7,8 @@ using Tiku.Domain.Common; using Tiku.Domain.Operations; using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Persistence; +using Tiku.Application.Security; +using Tiku.Application.Tenancy; namespace Tiku.Api.Controllers; @@ -14,7 +16,11 @@ namespace Tiku.Api.Controllers; [AllowAnonymous] [Produces("application/json")] [Route("api/tenant")] -public sealed class TenantPublicController(TikuDbContext dbContext) : ControllerBase +public sealed class TenantPublicController( + TikuDbContext dbContext, + ITenantContext tenantContext, + ITenantContextInitializer tenantContextInitializer, + ITenantDirectory tenantDirectory) : ControllerBase { [HttpGet("resolve")] [EndpointSummary("解析当前租户")] @@ -28,9 +34,23 @@ public sealed class TenantPublicController(TikuDbContext dbContext) : Controller var tenantCode = NormalizeTenantCode(query.TenantCode ?? Request.Headers["x-tenant-code"].FirstOrDefault()); var host = NormalizeHost(query.Host ?? Request.Host.Host); - var tenant = !string.IsNullOrWhiteSpace(tenantCode) - ? await FindActiveTenantByCodeAsync(tenantCode, cancellationToken) - : await FindActiveTenantByHostAsync(host, cancellationToken); + var directoryEntry = tenantContext.ResolutionSource == TenantResolutionSource.Host + ? await tenantDirectory.FindByHostAsync(Request.Host.Host, cancellationToken) + : !string.IsNullOrWhiteSpace(tenantCode) + ? await tenantDirectory.FindByCodeAsync(tenantCode, cancellationToken) + : string.IsNullOrWhiteSpace(host) + ? null + : await tenantDirectory.FindByHostAsync(host, cancellationToken); + + TenantLookupResult? tenant = directoryEntry is null + ? null + : new TenantLookupResult( + directoryEntry.TenantId, + directoryEntry.TenantCode, + directoryEntry.Name, + directoryEntry.Status, + directoryEntry.Mode, + directoryEntry.Host); if (tenant is null) { @@ -42,6 +62,11 @@ public sealed class TenantPublicController(TikuDbContext dbContext) : Controller }); } + tenantContextInitializer.Initialize( + tenant.Id, + tenant.Slug, + tenant.Host is null ? TenantResolutionSource.TenantCode : TenantResolutionSource.Host); + return Ok(await BuildResponseAsync(tenant, tenant.Host, cancellationToken)); } diff --git a/Tiku.Api/Controllers/TenantsController.cs b/Tiku.Api/Controllers/TenantsController.cs index 2ad8e1f..5301510 100644 --- a/Tiku.Api/Controllers/TenantsController.cs +++ b/Tiku.Api/Controllers/TenantsController.cs @@ -14,7 +14,7 @@ namespace Tiku.Api.Controllers; [Route("api/tenants")] public sealed class TenantsController( ICurrentUser currentUser, - ICurrentTenant currentTenant, + ITenantContext currentTenant, TikuDbContext dbContext) : ControllerBase { [HttpGet("current")] diff --git a/Tiku.Api/Middleware/CurrentPrincipalMiddleware.cs b/Tiku.Api/Middleware/CurrentPrincipalMiddleware.cs index a1bb3b9..4f327e7 100644 --- a/Tiku.Api/Middleware/CurrentPrincipalMiddleware.cs +++ b/Tiku.Api/Middleware/CurrentPrincipalMiddleware.cs @@ -6,11 +6,9 @@ public sealed class CurrentPrincipalMiddleware(RequestDelegate next) { public async Task InvokeAsync( HttpContext context, - ICurrentUser currentUser, - ICurrentTenant currentTenant) + ICurrentUser currentUser) { currentUser.Load(context.User); - currentTenant.Load(context.User); await next(context); } } diff --git a/Tiku.Api/Middleware/ExceptionHandlingMiddleware.cs b/Tiku.Api/Middleware/ExceptionHandlingMiddleware.cs index 3efe1ac..7c4cfe4 100644 --- a/Tiku.Api/Middleware/ExceptionHandlingMiddleware.cs +++ b/Tiku.Api/Middleware/ExceptionHandlingMiddleware.cs @@ -2,10 +2,12 @@ using Microsoft.AspNetCore.Mvc; using Tiku.Api.Controllers; using Tiku.Application.Assets; using Tiku.Application.Auth; +using Tiku.Application.Security; using Tiku.Application.Commerce; using Tiku.Application.Content; using Tiku.Application.Growth; using Tiku.Application.Points; +using Tiku.Application.QuestionBanks; using Tiku.Application.Storage; using Tiku.Infrastructure.Content; using Tiku.Infrastructure.Learning; @@ -13,6 +15,7 @@ using Tiku.Infrastructure.Profile; using Tiku.Infrastructure.QuestionBanks; using Tiku.Infrastructure.Scoreline; using Tiku.Application.TenantAdmin; +using Tiku.Application.Tenancy; namespace Tiku.Api.Middleware; @@ -45,6 +48,52 @@ public sealed class ExceptionHandlingMiddleware( return; } + if (exception is TenantContextConflictException) + { + await WriteProblemAsync( + context, + exception.Message, + StatusCodes.Status403Forbidden, + "tenant_context_conflict"); + return; + } + + if (exception is TenantFrontendConfigException frontendConfigException) + { + var status = frontendConfigException.Code switch + { + "tenant_not_found" or "frontend_config_not_found" => StatusCodes.Status404NotFound, + "frontend_config_version_conflict" => StatusCodes.Status409Conflict, + _ => StatusCodes.Status400BadRequest + }; + await WriteProblemAsync( + context, + frontendConfigException.Message, + status, + frontendConfigException.Code); + return; + } + + if (exception is PublicQuestionAccessDeniedException publicQuestionAccessDeniedException) + { + await WriteProblemAsync( + context, + publicQuestionAccessDeniedException.Message, + StatusCodes.Status403Forbidden, + publicQuestionAccessDeniedException.Code); + return; + } + + if (exception is QuestionLocatorException questionLocatorException) + { + await WriteProblemAsync( + context, + questionLocatorException.Message, + StatusCodes.Status404NotFound, + questionLocatorException.Code); + return; + } + if (exception is RequiredFieldException) { await WriteProblemAsync( diff --git a/Tiku.Api/Middleware/TenantResolutionMiddleware.cs b/Tiku.Api/Middleware/TenantResolutionMiddleware.cs new file mode 100644 index 0000000..1c99100 --- /dev/null +++ b/Tiku.Api/Middleware/TenantResolutionMiddleware.cs @@ -0,0 +1,74 @@ +using Microsoft.Extensions.Options; +using Microsoft.AspNetCore.Mvc; +using Tiku.Api.Options; +using Tiku.Application.Security; +using Tiku.Application.Tenancy; + +namespace Tiku.Api.Middleware; + +public sealed class TenantResolutionMiddleware(RequestDelegate next) +{ + public async Task InvokeAsync( + HttpContext context, + ITenantDirectory tenantDirectory, + ITenantContextInitializer tenantInitializer, + IOptions options) + { + var host = NormalizeHost(context.Request.Host.Host); + var isPlatformHost = options.Value.PlatformHosts.Any(candidate => + string.Equals(NormalizeHost(candidate), host, StringComparison.OrdinalIgnoreCase)); + + TenantDirectoryEntry? tenant = null; + if (!isPlatformHost && host is not null) + { + tenant = await tenantDirectory.FindByHostAsync(host, context.RequestAborted); + if (tenant is null && !IsExemptPath(context.Request.Path, options.Value.ExemptPathPrefixes)) + { + context.Response.StatusCode = StatusCodes.Status404NotFound; + await context.Response.WriteAsJsonAsync(new ProblemDetails + { + Title = "Tenant was not found.", + Status = StatusCodes.Status404NotFound, + Detail = "The request host is not assigned to an active tenant." + }, context.RequestAborted); + return; + } + } + else if (isPlatformHost && IsAllowedTenantCodePath( + context.Request.Path, + options.Value.TenantCodePathPrefixes)) + { + var tenantCode = context.Request.Headers["x-tenant-code"].FirstOrDefault() + ?? context.Request.Query["tenantCode"].FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(tenantCode)) + { + tenant = await tenantDirectory.FindByCodeAsync(tenantCode.Trim(), context.RequestAborted); + } + } + + if (tenant is not null) + { + tenantInitializer.Initialize( + tenant.TenantId, + tenant.TenantCode, + tenant.Host is null ? TenantResolutionSource.TenantCode : TenantResolutionSource.Host); + } + + await next(context); + } + + private static string? NormalizeHost(string? host) + { + return string.IsNullOrWhiteSpace(host) ? null : host.Trim().TrimEnd('.').ToLowerInvariant(); + } + + private static bool IsExemptPath(PathString path, IEnumerable prefixes) + { + return prefixes.Any(prefix => path.StartsWithSegments(prefix, StringComparison.OrdinalIgnoreCase)); + } + + private static bool IsAllowedTenantCodePath(PathString path, IEnumerable prefixes) + { + return prefixes.Any(prefix => path.StartsWithSegments(prefix, StringComparison.OrdinalIgnoreCase)); + } +} diff --git a/Tiku.Api/Options/TenantResolutionOptions.cs b/Tiku.Api/Options/TenantResolutionOptions.cs new file mode 100644 index 0000000..b9bccba --- /dev/null +++ b/Tiku.Api/Options/TenantResolutionOptions.cs @@ -0,0 +1,12 @@ +namespace Tiku.Api.Options; + +public sealed class TenantResolutionOptions +{ + public const string SectionName = "Tenancy:Resolution"; + + public string[] PlatformHosts { get; set; } = ["localhost", "127.0.0.1"]; + public string[] ExemptPathPrefixes { get; set; } = ["/health", "/openapi", "/scalar"]; + public string[] TenantCodePathPrefixes { get; set; } = + ["/api/auth", "/api/tenant", "/api/catalog", "/api/assets", "/api/scoreline", "/api/referral", "/api/commerce/payments/notify"]; + public string[] TrustedProxyAddresses { get; set; } = []; +} diff --git a/Tiku.Api/Program.cs b/Tiku.Api/Program.cs index 2d79f4f..22ca500 100644 --- a/Tiku.Api/Program.cs +++ b/Tiku.Api/Program.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.RateLimiting; +using Microsoft.AspNetCore.HttpOverrides; +using System.Net; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Scalar.AspNetCore; @@ -16,6 +18,7 @@ using Tiku.Api.Options; using Tiku.Api.Security; using Tiku.Application; using Tiku.Application.Security; +using Tiku.Application.Tenancy; using Tiku.Infrastructure; using Tiku.Infrastructure.Commerce; using Tiku.Infrastructure.Persistence; @@ -49,6 +52,30 @@ try }); builder.Services.AddProblemDetails(); builder.Services.AddApplication(); + builder.Services.AddOptions() + .Bind(builder.Configuration.GetSection(TenantResolutionOptions.SectionName)); + builder.Services.Configure(options => + { + options.ForwardedHeaders = + ForwardedHeaders.XForwardedFor | + ForwardedHeaders.XForwardedHost | + ForwardedHeaders.XForwardedProto; + options.ForwardLimit = 1; + options.KnownProxies.Clear(); + options.KnownIPNetworks.Clear(); + var resolution = builder.Configuration + .GetSection(TenantResolutionOptions.SectionName) + .Get() ?? new TenantResolutionOptions(); + foreach (var address in resolution.TrustedProxyAddresses) + { + if (IPAddress.TryParse(address, out var proxy)) + { + options.KnownProxies.Add(proxy); + } + } + }); + builder.Services.AddOptions() + .Bind(builder.Configuration.GetSection("TenantDomains")); builder.Services.AddOptions() .Bind(builder.Configuration.GetSection(CorsOptions.SectionName)) .ValidateDataAnnotations() @@ -218,6 +245,26 @@ try { OnTokenValidated = async context => { + var tenantIdValue = context.Principal?.FindFirst(TikuClaimTypes.TenantId)?.Value; + if (!Guid.TryParse(tenantIdValue, out var tenantId)) + { + context.Fail("Missing tenant claim."); + return; + } + + var tenantInitializer = context.HttpContext.RequestServices + .GetRequiredService(); + try + { + tenantInitializer.Initialize(tenantId, null, TenantResolutionSource.Jwt); + } + catch (TenantContextConflictException) + { + context.HttpContext.Items["tenant_context_conflict"] = true; + context.Fail("Authenticated tenant does not match the request host."); + return; + } + if (!jwtOptions.ValidateSessions) { return; @@ -242,6 +289,20 @@ try { context.Fail("Session has been revoked or expired."); } + }, + OnChallenge = async context => + { + if (context.HttpContext.Items.ContainsKey("tenant_context_conflict")) + { + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status403Forbidden; + await context.Response.WriteAsJsonAsync(new ProblemDetails + { + Title = "Authenticated tenant does not match the request host.", + Status = StatusCodes.Status403Forbidden, + Extensions = { ["code"] = "tenant_context_conflict" } + }); + } } }; }); @@ -276,9 +337,11 @@ try app.UseSerilogRequestLogging(SerilogRequestLogging.ConfigureRequestLogging); app.UseMiddleware(); + app.UseForwardedHeaders(); app.UseHttpsRedirection(); app.UseRouting(); app.UseCors(CorsOptions.PolicyName); + app.UseMiddleware(); app.UseAuthentication(); if (rateLimitOptions.Enabled) { diff --git a/Tiku.Application/Auth/ISessionService.cs b/Tiku.Application/Auth/ISessionService.cs index b5f3cbd..b40c3eb 100644 --- a/Tiku.Application/Auth/ISessionService.cs +++ b/Tiku.Application/Auth/ISessionService.cs @@ -4,7 +4,8 @@ namespace Tiku.Application.Auth; public interface ISessionService { - string GenerateRefreshToken(); + string GenerateRefreshToken(Guid tenantId, Guid sessionId); + bool TryParseRefreshToken(string refreshToken, out RefreshTokenLocator locator); string HashRefreshToken(string refreshToken); Task IssueAsync( @@ -17,3 +18,5 @@ public interface ISessionService string? userAgent, CancellationToken cancellationToken = default); } + +public readonly record struct RefreshTokenLocator(Guid TenantId, Guid SessionId); diff --git a/Tiku.Application/Catalog/TaxonomyModels.cs b/Tiku.Application/Catalog/TaxonomyModels.cs new file mode 100644 index 0000000..756ef3f --- /dev/null +++ b/Tiku.Application/Catalog/TaxonomyModels.cs @@ -0,0 +1,36 @@ +using System.Text.Json; +using Tiku.Domain.Catalog; +using Tiku.Domain.Content; + +namespace Tiku.Application.Catalog; + +public sealed record TaxonomyNodeItem( + Guid Id, + QuestionSource Source, + Guid? ParentId, + QuestionSource? ParentSource, + TaxonomyNodeType NodeType, + string Code, + string Name, + string? Path, + int Depth, + int SortOrder, + JsonElement Metadata); + +public sealed record CreateTaxonomyNodeCommand( + Guid? ParentId, + QuestionSource? ParentSource, + TaxonomyNodeType NodeType, + string Code, + string Name, + int SortOrder, + JsonElement Metadata); + +public interface ITaxonomyService +{ + Task> ListAsync(Guid tenantId, CancellationToken cancellationToken = default); + Task CreateAsync( + Guid tenantId, + CreateTaxonomyNodeCommand command, + CancellationToken cancellationToken = default); +} diff --git a/Tiku.Application/Content/ContentManagementModels.cs b/Tiku.Application/Content/ContentManagementModels.cs index 717d16f..7ffc933 100644 --- a/Tiku.Application/Content/ContentManagementModels.cs +++ b/Tiku.Application/Content/ContentManagementModels.cs @@ -1,6 +1,7 @@ using System.Text.Json; using Tiku.Application.Catalog; using Tiku.Domain.Content; +using Tiku.Application.QuestionBanks; namespace Tiku.Application.Content; @@ -79,7 +80,7 @@ public sealed record ReplaceCollectionItemsCommand( IReadOnlyCollection Questions); public sealed record CollectionQuestionCommand( - Guid QuestionId, + QuestionLocator Locator, string? SectionKey, int? Order, decimal? Score, @@ -173,6 +174,7 @@ public sealed record QuestionCollectionItemManagementItem( Guid Id, Guid CollectionId, Guid QuestionId, + QuestionLocator Locator, string? SectionKey, int Order, decimal? Score, diff --git a/Tiku.Application/DependencyInjection.cs b/Tiku.Application/DependencyInjection.cs index cabe458..ca867be 100644 --- a/Tiku.Application/DependencyInjection.cs +++ b/Tiku.Application/DependencyInjection.cs @@ -8,7 +8,9 @@ public static class DependencyInjection public static IServiceCollection AddApplication(this IServiceCollection services) { services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(provider => provider.GetRequiredService()); + services.AddScoped(provider => provider.GetRequiredService()); return services; } diff --git a/Tiku.Application/Learning/LearningActivityModels.cs b/Tiku.Application/Learning/LearningActivityModels.cs index e7e30ec..4148c32 100644 --- a/Tiku.Application/Learning/LearningActivityModels.cs +++ b/Tiku.Application/Learning/LearningActivityModels.cs @@ -1,6 +1,7 @@ using System.Text.Json; using Tiku.Domain.Content; using Tiku.Domain.Learning; +using Tiku.Application.QuestionBanks; namespace Tiku.Application.Learning; @@ -9,13 +10,12 @@ public sealed record LearningActor(Guid TenantId, Guid UserId); public sealed record LearningList(IReadOnlyCollection Items); public sealed record SubmitAnswerCommand( - Guid QuestionId, - Guid? PracticeSessionId, + Guid SessionQuestionId, IReadOnlyCollection? SelectedOptions, string? AnswerText, bool? SelfJudgedCorrect); -public sealed record QuestionActionCommand(Guid QuestionId, bool? Favorite); +public sealed record QuestionActionCommand(QuestionLocator Locator, bool? Favorite); public sealed record WordProgressCommand( Guid WordId, @@ -55,7 +55,11 @@ public sealed record LearningLeaderboardResult( LearningLeaderboardItem? CurrentUser, DateTimeOffset GeneratedAt); -public sealed record WrongQuestionReviewPlanItem(Guid QuestionId, int WrongCount, DateTimeOffset LastWrongAt); +public sealed record WrongQuestionReviewPlanItem( + Guid QuestionReferenceId, + QuestionLocator Locator, + int WrongCount, + DateTimeOffset LastWrongAt); public sealed record WrongQuestionReviewPlan( IReadOnlyCollection Items, @@ -106,21 +110,21 @@ public sealed record PracticeSessionFilter( public sealed record AnswerRecordItem( Guid Id, - Guid QuestionId, - Guid? QuestionVersionId, - Guid? PracticeSessionId, + Guid SessionQuestionId, + Guid PracticeSessionId, JsonElement SelectedOptions, string? AnswerText, bool? IsCorrect, DateTimeOffset AnsweredAt); public sealed record FavoriteQuestionItem( - Guid QuestionId, - string Source, + Guid QuestionReferenceId, + QuestionLocator Locator, DateTimeOffset CreatedAt); public sealed record WrongQuestionItem( - Guid QuestionId, + Guid QuestionReferenceId, + QuestionLocator Locator, int WrongCount, DateTimeOffset LastWrongAt, DateTimeOffset? ResolvedAt); @@ -154,7 +158,6 @@ public sealed record PracticeSessionItem( Guid? CollectionId, Guid? EntryId, Guid? ContentNodeId, - JsonElement QuestionIds, int QuestionCount, int? DurationMinutes, decimal? TotalScore, @@ -171,10 +174,13 @@ public sealed record PracticeSessionItem( public sealed record PracticeSessionDetailItem( PracticeSessionItem Session, IReadOnlyCollection Questions, - IReadOnlyDictionary AnswersByQuestion); + IReadOnlyDictionary AnswersBySessionQuestion); public sealed record PracticeSessionQuestionItem( - Guid Id, + Guid SessionQuestionId, + Guid QuestionReferenceId, + QuestionLocator Locator, + Guid QuestionId, string Type, string? TypeLabel, int? Difficulty, diff --git a/Tiku.Application/QuestionBanks/IQuestionReferenceService.cs b/Tiku.Application/QuestionBanks/IQuestionReferenceService.cs new file mode 100644 index 0000000..4557bbe --- /dev/null +++ b/Tiku.Application/QuestionBanks/IQuestionReferenceService.cs @@ -0,0 +1,27 @@ +using Tiku.Domain.Content; + +namespace Tiku.Application.QuestionBanks; + +public interface IPublicQuestionAccessPolicy +{ + Task EnsureCanStartAsync(Guid tenantId, CancellationToken cancellationToken = default); +} + +public interface IQuestionReferenceService +{ + Task ResolveAsync( + Guid tenantId, + Guid? userId, + QuestionLocator locator, + CancellationToken cancellationToken = default); +} + +public sealed class PublicQuestionAccessDeniedException(string code, string message) : Exception(message) +{ + public string Code { get; } = code; +} + +public sealed class QuestionLocatorException(string code, string message) : Exception(message) +{ + public string Code { get; } = code; +} diff --git a/Tiku.Application/QuestionBanks/QuestionBankQueryModels.cs b/Tiku.Application/QuestionBanks/QuestionBankQueryModels.cs index b21ce6b..79d8529 100644 --- a/Tiku.Application/QuestionBanks/QuestionBankQueryModels.cs +++ b/Tiku.Application/QuestionBanks/QuestionBankQueryModels.cs @@ -1,5 +1,6 @@ using System.Text.Json; using Tiku.Domain.QuestionBanks; +using Tiku.Domain.Content; namespace Tiku.Application.QuestionBanks; @@ -17,13 +18,14 @@ public sealed record QuestionBankFilter( IReadOnlyCollection? QuestionIds = null, string? Type = null, string? Keyword = null, - int? Limit = null); + int? Limit = null, + QuestionSource? Source = null); public sealed record QuestionBankCatalogItem( Guid Id, Guid? RegionId, string Name, - QuestionBankScope SourceScope, + QuestionSource Source, QuestionBankStatus Status, JsonElement Metadata); @@ -58,7 +60,10 @@ public sealed record QuestionCatalogItem( string? Explanation, JsonElement SubQuestions, string? CodeLang, - string? CodeTemplate); + string? CodeTemplate, + QuestionLocator Locator); + +public sealed record QuestionLocator(QuestionSource Source, Guid QuestionId); public sealed record QuestionVersionCatalogItem( Guid Id, diff --git a/Tiku.Application/Security/CurrentTenant.cs b/Tiku.Application/Security/CurrentTenant.cs deleted file mode 100644 index fd5547f..0000000 --- a/Tiku.Application/Security/CurrentTenant.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Security.Claims; - -namespace Tiku.Application.Security; - -public sealed class CurrentTenant : ICurrentTenant -{ - public Guid? TenantId { get; private set; } - public string? Role { get; private set; } - public bool IsResolved => TenantId.HasValue; - - public void Load(ClaimsPrincipal principal) - { - TenantId = principal.FindGuid(TikuClaimTypes.TenantId); - Role = principal.FindValue(TikuClaimTypes.TenantRole); - } -} diff --git a/Tiku.Application/Security/CurrentUser.cs b/Tiku.Application/Security/CurrentUser.cs index 30b9432..7f318eb 100644 --- a/Tiku.Application/Security/CurrentUser.cs +++ b/Tiku.Application/Security/CurrentUser.cs @@ -8,6 +8,7 @@ public sealed class CurrentUser : ICurrentUser public Guid? SessionId { get; private set; } public string? Phone { get; private set; } public string? Email { get; private set; } + public string? TenantRole { get; private set; } public bool IsAuthenticated { get; private set; } public void Load(ClaimsPrincipal principal) @@ -17,5 +18,6 @@ public sealed class CurrentUser : ICurrentUser SessionId = principal.FindGuid(TikuClaimTypes.SessionId); Phone = principal.FindValue(TikuClaimTypes.Phone); Email = principal.FindValue(TikuClaimTypes.Email); + TenantRole = principal.FindValue(TikuClaimTypes.TenantRole); } } diff --git a/Tiku.Application/Security/ICurrentTenant.cs b/Tiku.Application/Security/ICurrentTenant.cs deleted file mode 100644 index c5fafe4..0000000 --- a/Tiku.Application/Security/ICurrentTenant.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Security.Claims; - -namespace Tiku.Application.Security; - -public interface ICurrentTenant -{ - Guid? TenantId { get; } - string? Role { get; } - bool IsResolved { get; } - void Load(ClaimsPrincipal principal); -} diff --git a/Tiku.Application/Security/ICurrentUser.cs b/Tiku.Application/Security/ICurrentUser.cs index 3539862..58741b0 100644 --- a/Tiku.Application/Security/ICurrentUser.cs +++ b/Tiku.Application/Security/ICurrentUser.cs @@ -8,6 +8,7 @@ public interface ICurrentUser Guid? SessionId { get; } string? Phone { get; } string? Email { get; } + string? TenantRole { get; } bool IsAuthenticated { get; } void Load(ClaimsPrincipal principal); } diff --git a/Tiku.Application/Security/ITenantContext.cs b/Tiku.Application/Security/ITenantContext.cs new file mode 100644 index 0000000..fc8dcb0 --- /dev/null +++ b/Tiku.Application/Security/ITenantContext.cs @@ -0,0 +1,87 @@ +namespace Tiku.Application.Security; + +public enum TenantResolutionSource +{ + None, + Host, + TenantCode, + Jwt, + RefreshToken, + System +} + +public interface ITenantContext +{ + Guid? TenantId { get; } + string? TenantCode { get; } + TenantResolutionSource ResolutionSource { get; } + bool IsResolved { get; } + bool IsSystem { get; } +} + +public interface ITenantContextInitializer +{ + void Initialize(Guid tenantId, string? tenantCode, TenantResolutionSource source); + void InitializeSystem(Guid? targetTenantId, string reason); +} + +public sealed class TenantContext : ITenantContext, ITenantContextInitializer +{ + public Guid? TenantId { get; private set; } + public string? TenantCode { get; private set; } + public TenantResolutionSource ResolutionSource { get; private set; } + public bool IsResolved => TenantId.HasValue; + public bool IsSystem { get; private set; } + internal string? SystemReason { get; private set; } + + public void Initialize(Guid tenantId, string? tenantCode, TenantResolutionSource source) + { + if (tenantId == Guid.Empty) + { + throw new ArgumentException("Tenant ID cannot be empty.", nameof(tenantId)); + } + + if (IsSystem) + { + throw new InvalidOperationException("A system tenant context cannot be replaced."); + } + + if (TenantId.HasValue && TenantId.Value != tenantId) + { + throw new TenantContextConflictException(TenantId.Value, tenantId); + } + + var wasResolved = TenantId.HasValue; + TenantId = tenantId; + TenantCode = string.IsNullOrWhiteSpace(tenantCode) ? TenantCode : tenantCode.Trim(); + if (!wasResolved) + { + ResolutionSource = source; + } + } + + public void InitializeSystem(Guid? targetTenantId, string reason) + { + if (IsResolved || IsSystem) + { + throw new InvalidOperationException("Tenant context has already been initialized."); + } + + if (string.IsNullOrWhiteSpace(reason)) + { + throw new ArgumentException("A system scope requires an audit reason.", nameof(reason)); + } + + TenantId = targetTenantId; + ResolutionSource = TenantResolutionSource.System; + IsSystem = true; + SystemReason = reason.Trim(); + } +} + +public sealed class TenantContextConflictException(Guid expectedTenantId, Guid actualTenantId) + : Exception($"Resolved tenant '{expectedTenantId}' conflicts with authenticated tenant '{actualTenantId}'.") +{ + public Guid ExpectedTenantId { get; } = expectedTenantId; + public Guid ActualTenantId { get; } = actualTenantId; +} diff --git a/Tiku.Application/Security/ITenantExecutionScope.cs b/Tiku.Application/Security/ITenantExecutionScope.cs new file mode 100644 index 0000000..41d77cb --- /dev/null +++ b/Tiku.Application/Security/ITenantExecutionScope.cs @@ -0,0 +1,16 @@ +namespace Tiku.Application.Security; + +public interface ITenantExecutionScope +{ + Task ExecuteAsync( + Guid? targetTenantId, + string reason, + Func operation, + CancellationToken cancellationToken = default); + + Task ExecuteAsync( + Guid? targetTenantId, + string reason, + Func> operation, + CancellationToken cancellationToken = default); +} diff --git a/Tiku.Application/Tenancy/TenantDirectoryModels.cs b/Tiku.Application/Tenancy/TenantDirectoryModels.cs new file mode 100644 index 0000000..922d98d --- /dev/null +++ b/Tiku.Application/Tenancy/TenantDirectoryModels.cs @@ -0,0 +1,22 @@ +using Tiku.Domain.Tenancy; + +namespace Tiku.Application.Tenancy; + +public sealed record TenantDirectoryEntry( + Guid TenantId, + string TenantCode, + string Name, + TenantStatus Status, + TenantMode Mode, + string? Host); + +public interface ITenantDirectory +{ + Task FindByHostAsync( + string host, + CancellationToken cancellationToken = default); + + Task FindByCodeAsync( + string tenantCode, + CancellationToken cancellationToken = default); +} diff --git a/Tiku.Application/Tenancy/TenantDomainLifecycleModels.cs b/Tiku.Application/Tenancy/TenantDomainLifecycleModels.cs new file mode 100644 index 0000000..6b49f79 --- /dev/null +++ b/Tiku.Application/Tenancy/TenantDomainLifecycleModels.cs @@ -0,0 +1,39 @@ +namespace Tiku.Application.Tenancy; + +public sealed class DomainLifecycleOptions +{ + public bool Enabled { get; set; } = true; + public int PollSeconds { get; set; } = 60; + public int BatchSize { get; set; } = 50; + public string DnsJsonEndpoint { get; set; } = "https://cloudflare-dns.com/dns-query"; + public string VerificationRecordPrefix { get; set; } = "_tiku-verification"; + public string[] AllowedCnameTargets { get; set; } = []; + public string? GatewayBaseUrl { get; set; } + public string? GatewayApiKey { get; set; } +} + +public sealed record DomainOwnershipResult(bool Verified, bool Configured, string? FailureReason); +public sealed record DomainGatewayResult(bool TlsReady, bool Configured, string? FailureReason); + +public interface IDomainOwnershipVerifier +{ + Task VerifyAsync( + string host, + string verificationToken, + CancellationToken cancellationToken = default); +} + +public interface IDomainGatewayProvisioner +{ + Task EnsureTlsAsync(string host, CancellationToken cancellationToken = default); +} + +public interface ITenantDomainLifecycleService +{ + Task ProcessPendingAsync(CancellationToken cancellationToken = default); +} + +public interface ITenantRuntimeCacheInvalidator +{ + void Invalidate(Guid tenantId); +} diff --git a/Tiku.Application/Tenancy/TenantFrontendConfigModels.cs b/Tiku.Application/Tenancy/TenantFrontendConfigModels.cs new file mode 100644 index 0000000..82f8bda --- /dev/null +++ b/Tiku.Application/Tenancy/TenantFrontendConfigModels.cs @@ -0,0 +1,47 @@ +using System.Text.Json; + +namespace Tiku.Application.Tenancy; + +public sealed record TenantFrontendConfigDraft( + JsonElement Branding, + JsonElement Theme, + JsonElement Features, + JsonElement Navigation, + JsonElement HomeModules); + +public sealed record TenantFrontendConfigItem( + int SchemaVersion, + int ConfigVersion, + TenantFrontendConfigDraft Published, + TenantFrontendConfigDraft Draft, + DateTimeOffset? PublishedAt); + +public sealed record TenantRuntimeBootstrap( + int SchemaVersion, + int ConfigVersion, + string TenantCode, + string TenantName, + JsonElement Branding, + JsonElement Theme, + JsonElement Features, + JsonElement Navigation, + JsonElement HomeModules); + +public interface ITenantFrontendConfigService +{ + Task GetAsync(Guid tenantId, CancellationToken cancellationToken = default); + Task SaveDraftAsync( + Guid tenantId, + TenantFrontendConfigDraft draft, + CancellationToken cancellationToken = default); + Task PublishAsync( + Guid tenantId, + int expectedVersion, + CancellationToken cancellationToken = default); + Task GetRuntimeAsync(Guid tenantId, CancellationToken cancellationToken = default); +} + +public sealed class TenantFrontendConfigException(string code, string message) : Exception(message) +{ + public string Code { get; } = code; +} diff --git a/Tiku.Application/TenantAdmin/TenantAdminDirectModels.cs b/Tiku.Application/TenantAdmin/TenantAdminDirectModels.cs index 8606975..e06e508 100644 --- a/Tiku.Application/TenantAdmin/TenantAdminDirectModels.cs +++ b/Tiku.Application/TenantAdmin/TenantAdminDirectModels.cs @@ -460,6 +460,10 @@ public sealed record TenantDomainItem( bool IsPrimary, string? VerificationToken, DateTimeOffset? VerifiedAt, + DateTimeOffset? LastCheckedAt, + DateTimeOffset? DnsVerifiedAt, + DateTimeOffset? TlsReadyAt, + string? LastFailureReason, DateTimeOffset CreatedAt, DateTimeOffset UpdatedAt); diff --git a/Tiku.DbMigrator/DesignTimeTikuDbContextFactory.cs b/Tiku.DbMigrator/DesignTimeTikuDbContextFactory.cs index 53c0037..9a8f2bf 100644 --- a/Tiku.DbMigrator/DesignTimeTikuDbContextFactory.cs +++ b/Tiku.DbMigrator/DesignTimeTikuDbContextFactory.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; +using Tiku.Application.Security; using Tiku.Infrastructure.Persistence; namespace Tiku.DbMigrator; @@ -17,6 +18,8 @@ public sealed class DesignTimeTikuDbContextFactory : IDesignTimeDbContextFactory npgsql.MigrationsAssembly(typeof(TikuDbContext).Assembly.FullName)) .Options; - return new TikuDbContext(options); + var tenantContext = new TenantContext(); + tenantContext.InitializeSystem(null, "EF Core design-time model generation"); + return new TikuDbContext(options, tenantContext); } } diff --git a/Tiku.DbMigrator/Program.cs b/Tiku.DbMigrator/Program.cs index 346f94c..b179761 100644 --- a/Tiku.DbMigrator/Program.cs +++ b/Tiku.DbMigrator/Program.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Tiku.Infrastructure; using Tiku.Infrastructure.Persistence; +using Tiku.Application; var builder = Host.CreateApplicationBuilder(args); var connectionString = @@ -12,6 +13,7 @@ var connectionString = throw new InvalidOperationException( "Database connection is required. Configure ConnectionStrings:Database or DATABASE_URL."); +builder.Services.AddApplication(); builder.Services.AddInfrastructure(connectionString); using var host = builder.Build(); diff --git a/Tiku.Domain/Catalog/TaxonomyEntities.cs b/Tiku.Domain/Catalog/TaxonomyEntities.cs new file mode 100644 index 0000000..33ccc22 --- /dev/null +++ b/Tiku.Domain/Catalog/TaxonomyEntities.cs @@ -0,0 +1,37 @@ +using System.Text.Json; +using Tiku.Domain.Common; + +namespace Tiku.Domain.Catalog; + +public sealed class TaxonomyNode : AuditableTenantEntity +{ + public Guid? ParentOwnerTenantId { get; set; } + public Guid? ParentId { get; set; } + public TaxonomyNodeType NodeType { get; set; } = TaxonomyNodeType.KnowledgePoint; + public string Code { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public string? Path { get; set; } + public int Depth { get; set; } + public int SortOrder { get; set; } + public bool IsActive { get; set; } = true; + public JsonElement Metadata { get; set; } = JsonDefaults.Object(); +} + +public sealed class QuestionTaxonomyAssignment : Entity, ITenantOwned +{ + public Guid TenantId { get; set; } + public Guid QuestionId { get; set; } + public Guid TaxonomyOwnerTenantId { get; set; } + public Guid TaxonomyNodeId { get; set; } + public bool IsPrimary { get; set; } + public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; +} + +public enum TaxonomyNodeType +{ + Subject, + Chapter, + KnowledgePoint, + Paper, + Custom +} diff --git a/Tiku.Domain/Commerce/CommerceEntities.cs b/Tiku.Domain/Commerce/CommerceEntities.cs index 31d050b..987c2a6 100644 --- a/Tiku.Domain/Commerce/CommerceEntities.cs +++ b/Tiku.Domain/Commerce/CommerceEntities.cs @@ -89,7 +89,7 @@ public sealed class Payment : AuditableTenantEntity public JsonElement RawPayload { get; set; } = JsonDefaults.Object(); } -public sealed class PaymentEvent : Entity +public sealed class PaymentEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid? PaymentId { get; set; } @@ -103,7 +103,7 @@ public sealed class PaymentEvent : Entity public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } -public sealed class Entitlement : Entity +public sealed class Entitlement : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid UserId { get; set; } @@ -213,7 +213,7 @@ public sealed class TenantSubscription : AuditableTenantEntity public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } -public sealed class TenantUsageRecord : Entity +public sealed class TenantUsageRecord : Entity, ITenantOwned { public Guid TenantId { get; set; } public string MetricKey { get; set; } = string.Empty; @@ -248,7 +248,7 @@ public sealed class CommerceRefundRequest : AuditableTenantEntity public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } -public sealed class CommerceRefundEvent : Entity +public sealed class CommerceRefundEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid RefundRequestId { get; set; } @@ -285,7 +285,7 @@ public sealed class CommerceReconciliationBatch : AuditableTenantEntity public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } -public sealed class CommerceReconciliationItem : Entity +public sealed class CommerceReconciliationItem : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid BatchId { get; set; } @@ -344,7 +344,7 @@ public sealed class CommerceReconciliationIssue : AuditableTenantEntity public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } -public sealed class CommerceReconciliationIssueEvent : Entity +public sealed class CommerceReconciliationIssueEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid IssueId { get; set; } diff --git a/Tiku.Domain/Common/Entity.cs b/Tiku.Domain/Common/Entity.cs index dac48d5..a2b2f53 100644 --- a/Tiku.Domain/Common/Entity.cs +++ b/Tiku.Domain/Common/Entity.cs @@ -13,13 +13,18 @@ public interface IHasTimestamps DateTimeOffset UpdatedAt { get; set; } } +public interface ITenantOwned +{ + Guid TenantId { get; set; } +} + public abstract class AuditableEntity : Entity, IHasTimestamps { public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow; } -public abstract class TenantEntity : Entity +public abstract class TenantEntity : Entity, ITenantOwned { public Guid TenantId { get; set; } } diff --git a/Tiku.Domain/Content/ContentEntities.cs b/Tiku.Domain/Content/ContentEntities.cs index 810cd6f..18f6257 100644 --- a/Tiku.Domain/Content/ContentEntities.cs +++ b/Tiku.Domain/Content/ContentEntities.cs @@ -141,6 +141,8 @@ public enum ContentStatus public sealed class QuestionCollectionItem : AuditableTenantEntity { public Guid CollectionId { get; set; } + public Guid QuestionReferenceId { get; set; } + public Guid QuestionOwnerTenantId { get; set; } public Guid QuestionId { get; set; } public string? SectionKey { get; set; } public int SortOrder { get; set; } diff --git a/Tiku.Domain/Content/ContentPlatformEntities.cs b/Tiku.Domain/Content/ContentPlatformEntities.cs index b8d168d..847b17d 100644 --- a/Tiku.Domain/Content/ContentPlatformEntities.cs +++ b/Tiku.Domain/Content/ContentPlatformEntities.cs @@ -3,7 +3,7 @@ using Tiku.Domain.Common; namespace Tiku.Domain.Content; -public sealed class ContentAssetAccessEvent : Entity +public sealed class ContentAssetAccessEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid? AssetId { get; set; } @@ -24,7 +24,7 @@ public sealed class ContentAssetAccessEvent : Entity public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } -public sealed class ContentAssetSecurityScanEvent : Entity +public sealed class ContentAssetSecurityScanEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid? AssetId { get; set; } @@ -36,38 +36,25 @@ public sealed class ContentAssetSecurityScanEvent : Entity public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } -public sealed class QuestionBankGrant : AuditableEntity +public sealed class TenantQuestionBankPreference : AuditableTenantEntity { - public Guid SourceQuestionBankId { get; set; } + public Guid QuestionBankOwnerTenantId { get; set; } + public Guid QuestionBankId { get; set; } + public bool IsVisible { get; set; } = true; + public string? Alias { get; set; } + public int SortOrder { get; set; } + public string? NavigationLocation { get; set; } public Guid? CreatedBy { get; set; } public Guid? UpdatedBy { get; set; } - public QuestionBankGrantScope GrantScope { get; set; } = QuestionBankGrantScope.Plans; - public string[] AllowedPlanCodes { get; set; } = []; - public Guid[] AllowedTenantIds { get; set; } = []; - public Guid[] AllowedRegionIds { get; set; } = []; - public Guid[] AllowedSubjectIds { get; set; } = []; - public QuestionBankGrantStatus Status { get; set; } = QuestionBankGrantStatus.Active; - public DateTimeOffset? StartsAt { get; set; } - public DateTimeOffset? ExpiresAt { get; set; } public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } -public sealed class TenantQuestionBankAdoption : AuditableTenantEntity +public sealed class TenantQuestionReference : AuditableTenantEntity { - public Guid SourceQuestionBankId { get; set; } - public Guid? GrantId { get; set; } - public Guid? TargetQuestionBankId { get; set; } - public Guid? TargetEntryId { get; set; } - public Guid? TargetCollectionId { get; set; } + public Guid QuestionOwnerTenantId { get; set; } + public Guid QuestionId { get; set; } + public QuestionSource Source { get; set; } = QuestionSource.Tenant; public Guid? CreatedBy { get; set; } - public Guid? UpdatedBy { get; set; } - public QuestionBankAdoptionMode AdoptionMode { get; set; } = QuestionBankAdoptionMode.CopiedSnapshot; - public QuestionBankAdoptionStatus Status { get; set; } = QuestionBankAdoptionStatus.Active; - public QuestionBankAdoptionSyncStatus SyncStatus { get; set; } = QuestionBankAdoptionSyncStatus.Synced; - public JsonElement SourceSnapshot { get; set; } = JsonDefaults.Object(); - public int CopiedQuestionCount { get; set; } - public JsonElement Metadata { get; set; } = JsonDefaults.Object(); - public DateTimeOffset? LastSyncedAt { get; set; } } public sealed class AiRecommendationReport : AuditableTenantEntity @@ -90,9 +77,5 @@ public enum AssetAccessType { Download, Preview, AdminDownload, AdminPreview, Up public enum AssetAccessDisposition { Attachment, Inline } public enum AssetAccessResult { Granted, Denied } public enum AssetSecurityRiskLevel { None, Low, Medium, High, Critical } -public enum QuestionBankGrantScope { AllActiveTenants, Plans, Tenants, Mixed } -public enum QuestionBankGrantStatus { Active, Disabled, Expired } -public enum QuestionBankAdoptionMode { CopiedSnapshot, Reference } -public enum QuestionBankAdoptionStatus { Active, SyncPending, Suspended, Archived } -public enum QuestionBankAdoptionSyncStatus { Pending, Synced, Failed } +public enum QuestionSource { Platform, Tenant } public enum AiRecommendationReportStatus { Draft, Generated, Failed } diff --git a/Tiku.Domain/Growth/GrowthEntities.cs b/Tiku.Domain/Growth/GrowthEntities.cs index 4c54282..ce9ec4f 100644 --- a/Tiku.Domain/Growth/GrowthEntities.cs +++ b/Tiku.Domain/Growth/GrowthEntities.cs @@ -188,7 +188,7 @@ public sealed class CommissionSettlementProof : AuditableTenantEntity public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } -public sealed class CommissionSettlementExportEvent : Entity +public sealed class CommissionSettlementExportEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid SettlementId { get; set; } diff --git a/Tiku.Domain/Import/PocketBaseImportEntities.cs b/Tiku.Domain/Import/PocketBaseImportEntities.cs index 940c67f..bca94cf 100644 --- a/Tiku.Domain/Import/PocketBaseImportEntities.cs +++ b/Tiku.Domain/Import/PocketBaseImportEntities.cs @@ -3,7 +3,7 @@ using Tiku.Domain.Common; namespace Tiku.Domain.Import; -public sealed class PocketBaseImportRun : Entity +public sealed class PocketBaseImportRun : Entity, ITenantOwned { public Guid TenantId { get; set; } public string SourceName { get; set; } = string.Empty; @@ -14,7 +14,7 @@ public sealed class PocketBaseImportRun : Entity public DateTimeOffset? FinishedAt { get; set; } } -public sealed class PocketBaseRawRecord +public sealed class PocketBaseRawRecord : ITenantOwned { public Guid RunId { get; set; } public Guid TenantId { get; set; } @@ -26,7 +26,7 @@ public sealed class PocketBaseRawRecord public DateTimeOffset ImportedAt { get; set; } = DateTimeOffset.UtcNow; } -public sealed class PocketBaseImportIssue : Entity +public sealed class PocketBaseImportIssue : Entity, ITenantOwned { public Guid? RunId { get; set; } public Guid TenantId { get; set; } diff --git a/Tiku.Domain/Learning/LearningEntities.cs b/Tiku.Domain/Learning/LearningEntities.cs index 242e542..ee885e0 100644 --- a/Tiku.Domain/Learning/LearningEntities.cs +++ b/Tiku.Domain/Learning/LearningEntities.cs @@ -15,7 +15,6 @@ public sealed class PracticeSession : TenantEntity public Guid? TargetId { get; set; } public DateTimeOffset StartedAt { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset? FinishedAt { get; set; } - public JsonElement QuestionIds { get; set; } = JsonDefaults.Array(); public int QuestionCount { get; set; } public int? DurationMinutes { get; set; } public decimal? TotalScore { get; set; } @@ -27,6 +26,17 @@ public sealed class PracticeSession : TenantEntity public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } +public sealed class PracticeSessionQuestion : TenantEntity +{ + public Guid PracticeSessionId { get; set; } + public Guid QuestionReferenceId { get; set; } + public Guid QuestionOwnerTenantId { get; set; } + public Guid QuestionId { get; set; } + public Guid QuestionVersionId { get; set; } + public int Position { get; set; } + public decimal? Score { get; set; } +} + public enum PracticeAccessMode { Free, @@ -37,9 +47,8 @@ public enum PracticeAccessMode public sealed class AnswerRecord : TenantEntity { public Guid UserId { get; set; } - public Guid? QuestionId { get; set; } - public Guid? QuestionVersionId { get; set; } - public Guid? PracticeSessionId { get; set; } + public Guid PracticeSessionId { get; set; } + public Guid SessionQuestionId { get; set; } public string? LegacyId { get; set; } public string? LegacyQuestionId { get; set; } public string? LegacyCategoryId { get; set; } @@ -50,19 +59,23 @@ public sealed class AnswerRecord : TenantEntity public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } -public sealed class FavoriteQuestion +public sealed class FavoriteQuestion : ITenantOwned { public Guid TenantId { get; set; } public Guid UserId { get; set; } + public Guid QuestionReferenceId { get; set; } + public Guid QuestionOwnerTenantId { get; set; } public Guid QuestionId { get; set; } public string Source { get; set; } = "imported"; public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } -public sealed class WrongQuestion +public sealed class WrongQuestion : ITenantOwned { public Guid TenantId { get; set; } public Guid UserId { get; set; } + public Guid QuestionReferenceId { get; set; } + public Guid QuestionOwnerTenantId { get; set; } public Guid QuestionId { get; set; } public int WrongCount { get; set; } = 1; public DateTimeOffset LastWrongAt { get; set; } = DateTimeOffset.UtcNow; diff --git a/Tiku.Domain/Learning/LearningReportEntities.cs b/Tiku.Domain/Learning/LearningReportEntities.cs index b8e604a..361dd7c 100644 --- a/Tiku.Domain/Learning/LearningReportEntities.cs +++ b/Tiku.Domain/Learning/LearningReportEntities.cs @@ -36,7 +36,7 @@ public sealed class Report : AuditableTenantEntity public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } -public sealed class ReportStatusEvent : Entity +public sealed class ReportStatusEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid ReportId { get; set; } @@ -48,7 +48,7 @@ public sealed class ReportStatusEvent : Entity public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } -public sealed class UserScoreEvent : Entity +public sealed class UserScoreEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid UserId { get; set; } @@ -73,7 +73,7 @@ public sealed class PracticeDailyUsage : AuditableTenantEntity public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } -public sealed class PracticeAccessEvent : Entity +public sealed class PracticeAccessEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid? UserId { get; set; } diff --git a/Tiku.Domain/Platform/PlatformOperationsEntities.cs b/Tiku.Domain/Platform/PlatformOperationsEntities.cs index 2fcbdab..15f1188 100644 --- a/Tiku.Domain/Platform/PlatformOperationsEntities.cs +++ b/Tiku.Domain/Platform/PlatformOperationsEntities.cs @@ -18,7 +18,7 @@ public sealed class PlatformSaasPlan : AuditableEntity public int SortOrder { get; set; } } -public sealed class TenantBillingProfile : IHasTimestamps +public sealed class TenantBillingProfile : IHasTimestamps, ITenantOwned { public Guid TenantId { get; set; } public string? BillingName { get; set; } @@ -58,7 +58,7 @@ public sealed class TenantInvoice : AuditableTenantEntity public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } -public sealed class TenantInvoiceItem : Entity +public sealed class TenantInvoiceItem : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid InvoiceId { get; set; } diff --git a/Tiku.Domain/QuestionBanks/QuestionEntities.cs b/Tiku.Domain/QuestionBanks/QuestionEntities.cs index e4af5b7..6fba92d 100644 --- a/Tiku.Domain/QuestionBanks/QuestionEntities.cs +++ b/Tiku.Domain/QuestionBanks/QuestionEntities.cs @@ -7,17 +7,10 @@ public sealed class QuestionBank : AuditableTenantEntity { public Guid? RegionId { get; set; } public string Name { get; set; } = string.Empty; - public QuestionBankScope SourceScope { get; set; } = QuestionBankScope.Tenant; public QuestionBankStatus Status { get; set; } = QuestionBankStatus.Active; public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } -public enum QuestionBankScope -{ - Platform, - Tenant -} - public enum QuestionBankStatus { Active, @@ -55,7 +48,7 @@ public enum QuestionStatus Archived } -public sealed class QuestionVersion : Entity +public sealed class QuestionVersion : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid QuestionId { get; set; } diff --git a/Tiku.Domain/Tenancy/TenantConfigurationEntities.cs b/Tiku.Domain/Tenancy/TenantConfigurationEntities.cs index c8453b0..abf9dfd 100644 --- a/Tiku.Domain/Tenancy/TenantConfigurationEntities.cs +++ b/Tiku.Domain/Tenancy/TenantConfigurationEntities.cs @@ -11,6 +11,10 @@ public sealed class TenantDomain : AuditableTenantEntity public bool IsPrimary { get; set; } public string? VerificationToken { get; set; } public DateTimeOffset? VerifiedAt { get; set; } + public DateTimeOffset? LastCheckedAt { get; set; } + public DateTimeOffset? DnsVerifiedAt { get; set; } + public DateTimeOffset? TlsReadyAt { get; set; } + public string? LastFailureReason { get; set; } } public enum TenantDomainType @@ -28,7 +32,7 @@ public enum TenantDomainStatus Disabled } -public sealed class TenantBranding : IHasTimestamps +public sealed class TenantBranding : IHasTimestamps, ITenantOwned { public Guid TenantId { get; set; } public string BrandName { get; set; } = string.Empty; @@ -45,7 +49,7 @@ public sealed class TenantBranding : IHasTimestamps public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow; } -public sealed class TenantSettings : IHasTimestamps +public sealed class TenantSettings : IHasTimestamps, ITenantOwned { public Guid TenantId { get; set; } public JsonElement FeatureFlags { get; set; } = JsonDefaults.Object(); @@ -54,3 +58,20 @@ public sealed class TenantSettings : IHasTimestamps public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow; } + +public sealed class TenantFrontendConfig : AuditableTenantEntity +{ + public int SchemaVersion { get; set; } = 1; + public int ConfigVersion { get; set; } = 1; + public JsonElement PublishedBranding { get; set; } = JsonDefaults.Object(); + public JsonElement PublishedTheme { get; set; } = JsonDefaults.Object(); + public JsonElement PublishedFeatures { get; set; } = JsonDefaults.Object(); + public JsonElement PublishedNavigation { get; set; } = JsonDefaults.Array(); + public JsonElement PublishedHomeModules { get; set; } = JsonDefaults.Array(); + public JsonElement DraftBranding { get; set; } = JsonDefaults.Object(); + public JsonElement DraftTheme { get; set; } = JsonDefaults.Object(); + public JsonElement DraftFeatures { get; set; } = JsonDefaults.Object(); + public JsonElement DraftNavigation { get; set; } = JsonDefaults.Array(); + public JsonElement DraftHomeModules { get; set; } = JsonDefaults.Array(); + public DateTimeOffset? PublishedAt { get; set; } +} diff --git a/Tiku.Domain/Tenancy/TenantOperationsEntities.cs b/Tiku.Domain/Tenancy/TenantOperationsEntities.cs index bc69bd6..9d88d4e 100644 --- a/Tiku.Domain/Tenancy/TenantOperationsEntities.cs +++ b/Tiku.Domain/Tenancy/TenantOperationsEntities.cs @@ -26,7 +26,7 @@ public sealed class TenantSecret : AuditableTenantEntity public DateTimeOffset? ExpiresAt { get; set; } } -public sealed class SmsVerificationCode : Entity +public sealed class SmsVerificationCode : Entity, ITenantOwned { public Guid TenantId { get; set; } public string Phone { get; set; } = string.Empty; @@ -43,7 +43,7 @@ public sealed class SmsVerificationCode : Entity public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } -public sealed class AuthLoginEvent : Entity +public sealed class AuthLoginEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid? UserId { get; set; } @@ -69,7 +69,7 @@ public sealed class AuthSession : AuditableTenantEntity public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } -public sealed class SmsSendRateLimit +public sealed class SmsSendRateLimit : ITenantOwned { public Guid TenantId { get; set; } public SmsRateLimitDimension Dimension { get; set; } = SmsRateLimitDimension.Phone; diff --git a/Tiku.Infrastructure/Auth/AuthService.cs b/Tiku.Infrastructure/Auth/AuthService.cs index 4c2b550..5b78107 100644 --- a/Tiku.Infrastructure/Auth/AuthService.cs +++ b/Tiku.Infrastructure/Auth/AuthService.cs @@ -152,10 +152,19 @@ public sealed class AuthService( RefreshSessionRequest request, CancellationToken cancellationToken = default) { + if (!sessionService.TryParseRefreshToken(request.RefreshToken, out var locator)) + { + throw new SessionRevokedException(); + } + var tokenHash = sessionService.HashRefreshToken(request.RefreshToken); var now = DateTimeOffset.UtcNow; var session = await dbContext.AuthSessions - .SingleOrDefaultAsync(entity => entity.TokenHash == tokenHash, cancellationToken); + .SingleOrDefaultAsync(entity => + entity.Id == locator.SessionId && + entity.TenantId == locator.TenantId && + entity.TokenHash == tokenHash, + cancellationToken); if (session is null || session.RevokedAt is not null || session.ExpiresAt <= now) { @@ -194,9 +203,18 @@ public sealed class AuthService( LogoutSessionRequest request, CancellationToken cancellationToken = default) { + if (!sessionService.TryParseRefreshToken(request.RefreshToken, out var locator)) + { + return; + } + var tokenHash = sessionService.HashRefreshToken(request.RefreshToken); var session = await dbContext.AuthSessions - .SingleOrDefaultAsync(entity => entity.TokenHash == tokenHash, cancellationToken); + .SingleOrDefaultAsync(entity => + entity.Id == locator.SessionId && + entity.TenantId == locator.TenantId && + entity.TokenHash == tokenHash, + cancellationToken); if (session is null || session.RevokedAt is not null) { diff --git a/Tiku.Infrastructure/Auth/SessionService.cs b/Tiku.Infrastructure/Auth/SessionService.cs index 2733c39..4ca895c 100644 --- a/Tiku.Infrastructure/Auth/SessionService.cs +++ b/Tiku.Infrastructure/Auth/SessionService.cs @@ -16,9 +16,29 @@ public sealed class SessionService( { private readonly JwtOptions options = options.Value; - public string GenerateRefreshToken() + public string GenerateRefreshToken(Guid tenantId, Guid sessionId) { - return Base64UrlEncoder.Encode(RandomNumberGenerator.GetBytes(64)); + return $"v1.{tenantId:N}.{sessionId:N}.{Base64UrlEncoder.Encode(RandomNumberGenerator.GetBytes(64))}"; + } + + public bool TryParseRefreshToken(string refreshToken, out RefreshTokenLocator locator) + { + locator = default; + if (string.IsNullOrWhiteSpace(refreshToken)) + { + return false; + } + + var parts = refreshToken.Split('.', 4, StringSplitOptions.None); + if (parts.Length != 4 || parts[0] != "v1" || parts[3].Length < 32 || + !Guid.TryParseExact(parts[1], "N", out var tenantId) || + !Guid.TryParseExact(parts[2], "N", out var sessionId)) + { + return false; + } + + locator = new RefreshTokenLocator(tenantId, sessionId); + return true; } public string HashRefreshToken(string refreshToken) @@ -37,17 +57,19 @@ public sealed class SessionService( string? userAgent, CancellationToken cancellationToken = default) { - var refreshToken = GenerateRefreshToken(); var session = new AuthSession { + Id = Guid.NewGuid(), TenantId = membership.TenantId, UserId = userId, - TokenHash = HashRefreshToken(refreshToken), + TokenHash = string.Empty, Provider = provider, ExpiresAt = DateTimeOffset.UtcNow.AddDays(options.RefreshTokenDays), IpAddress = ipAddress, UserAgent = userAgent }; + var refreshToken = GenerateRefreshToken(session.TenantId, session.Id); + session.TokenHash = HashRefreshToken(refreshToken); dbContext.AuthSessions.Add(session); await dbContext.SaveChangesAsync(cancellationToken); diff --git a/Tiku.Infrastructure/Catalog/TaxonomyService.cs b/Tiku.Infrastructure/Catalog/TaxonomyService.cs new file mode 100644 index 0000000..59bf50e --- /dev/null +++ b/Tiku.Infrastructure/Catalog/TaxonomyService.cs @@ -0,0 +1,151 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Tiku.Application.Catalog; +using Tiku.Application.QuestionBanks; +using Tiku.Application.Security; +using Tiku.Domain.Catalog; +using Tiku.Domain.Content; +using Tiku.Domain.Tenancy; +using Tiku.Infrastructure.Persistence; + +namespace Tiku.Infrastructure.Catalog; + +public sealed class TaxonomyService( + TikuDbContext dbContext, + IPublicQuestionAccessPolicy accessPolicy, + ITenantExecutionScope tenantExecutionScope) : ITaxonomyService +{ + public async Task> ListAsync( + Guid tenantId, + CancellationToken cancellationToken = default) + { + var includePlatform = true; + try + { + await accessPolicy.EnsureCanStartAsync(tenantId, cancellationToken); + } + catch (PublicQuestionAccessDeniedException) + { + includePlatform = false; + } + + return await tenantExecutionScope.ExecuteAsync( + tenantId, + "List platform taxonomy with tenant extensions", + async (provider, token) => + { + var systemDbContext = provider.GetRequiredService(); + var platformTenantId = includePlatform + ? await systemDbContext.Tenants.AsNoTracking() + .Where(tenant => tenant.Mode == TenantMode.PlatformOwned) + .Select(tenant => (Guid?)tenant.Id) + .SingleOrDefaultAsync(token) + : null; + return await systemDbContext.TaxonomyNodes.AsNoTracking() + .Where(node => + node.IsActive && + (node.TenantId == tenantId || + (platformTenantId.HasValue && node.TenantId == platformTenantId.Value))) + .OrderBy(node => node.Depth) + .ThenBy(node => node.SortOrder) + .Select(node => new TaxonomyNodeItem( + node.Id, + node.TenantId == tenantId ? QuestionSource.Tenant : QuestionSource.Platform, + node.ParentId, + !node.ParentOwnerTenantId.HasValue + ? null + : node.ParentOwnerTenantId == tenantId + ? QuestionSource.Tenant + : QuestionSource.Platform, + node.NodeType, + node.Code, + node.Name, + node.Path, + node.Depth, + node.SortOrder, + node.Metadata)) + .ToArrayAsync(token); + }, + cancellationToken); + } + + public async Task CreateAsync( + Guid tenantId, + CreateTaxonomyNodeCommand command, + CancellationToken cancellationToken = default) + { + Guid? parentOwnerTenantId = null; + TaxonomyParent? parent = null; + if (command.ParentId.HasValue) + { + parentOwnerTenantId = command.ParentSource switch + { + QuestionSource.Tenant => tenantId, + QuestionSource.Platform => await ResolvePlatformTenantIdAsync(tenantId, cancellationToken), + _ => throw new InvalidOperationException("A parent source is required when parentId is provided.") + }; + parent = await tenantExecutionScope.ExecuteAsync( + tenantId, + "Validate taxonomy extension parent ownership", + async (provider, token) => + { + var systemDbContext = provider.GetRequiredService(); + return await systemDbContext.TaxonomyNodes.AsNoTracking() + .Where(node => + node.TenantId == parentOwnerTenantId && + node.Id == command.ParentId.Value && + node.IsActive) + .Select(node => new TaxonomyParent(node.Path, node.Depth)) + .SingleOrDefaultAsync(token); + }, + cancellationToken) + ?? throw new InvalidOperationException("Taxonomy parent was not found."); + } + + var node = new TaxonomyNode + { + TenantId = tenantId, + ParentOwnerTenantId = parentOwnerTenantId, + ParentId = command.ParentId, + NodeType = command.NodeType, + Code = command.Code.Trim(), + Name = command.Name.Trim(), + Depth = parent is null ? 0 : parent.Depth + 1, + SortOrder = command.SortOrder, + Metadata = command.Metadata.Clone() + }; + node.Path = parent is null + ? $"n{node.Id:N}" + : $"{parent.Path}.n{node.Id:N}"; + dbContext.TaxonomyNodes.Add(node); + await dbContext.SaveChangesAsync(cancellationToken); + return new TaxonomyNodeItem( + node.Id, + QuestionSource.Tenant, + node.ParentId, + node.ParentId.HasValue ? command.ParentSource : null, + node.NodeType, + node.Code, + node.Name, + node.Path, + node.Depth, + node.SortOrder, + node.Metadata); + } + + private async Task ResolvePlatformTenantIdAsync(Guid tenantId, CancellationToken cancellationToken) + { + await accessPolicy.EnsureCanStartAsync(tenantId, cancellationToken); + return await tenantExecutionScope.ExecuteAsync( + tenantId, + "Resolve platform taxonomy owner", + async (provider, token) => await provider.GetRequiredService() + .Tenants.AsNoTracking() + .Where(tenant => tenant.Mode == TenantMode.PlatformOwned) + .Select(tenant => tenant.Id) + .SingleAsync(token), + cancellationToken); + } + + private sealed record TaxonomyParent(string? Path, int Depth); +} diff --git a/Tiku.Infrastructure/Content/ContentManagementService.cs b/Tiku.Infrastructure/Content/ContentManagementService.cs index c2078d8..198091a 100644 --- a/Tiku.Infrastructure/Content/ContentManagementService.cs +++ b/Tiku.Infrastructure/Content/ContentManagementService.cs @@ -3,6 +3,7 @@ using System.Text.Json; using Microsoft.EntityFrameworkCore; using Tiku.Application.Catalog; using Tiku.Application.Content; +using Tiku.Application.QuestionBanks; using Tiku.Domain.Catalog; using Tiku.Domain.Common; using Tiku.Domain.Content; @@ -11,7 +12,9 @@ using Tiku.Infrastructure.Persistence; namespace Tiku.Infrastructure.Content; -public sealed class ContentManagementService(TikuDbContext dbContext) : IContentManagementService +public sealed class ContentManagementService( + TikuDbContext dbContext, + IQuestionReferenceService questionReferenceService) : IContentManagementService { private const int DefaultLimit = 100; private const int MaxLimit = 1000; @@ -358,15 +361,15 @@ public sealed class ContentManagementService(TikuDbContext dbContext) : IContent throw new ContentManagementException("Collection was not found.", "collection_not_found"); } - var questionIds = command.Questions.Select(item => item.QuestionId).Distinct().ToArray(); - var existingQuestions = await dbContext.Questions - .Where(question => question.TenantId == actor.TenantId && questionIds.Contains(question.Id)) - .Select(question => question.Id) - .ToArrayAsync(cancellationToken); - - if (existingQuestions.Length != questionIds.Length) + var resolvedQuestions = new List<(CollectionQuestionCommand Command, TenantQuestionReference Reference)>(); + foreach (var question in command.Questions) { - throw new ContentManagementException("One or more questions are not in this tenant.", "question_not_found"); + var reference = await questionReferenceService.ResolveAsync( + actor.TenantId, + actor.UserId, + question.Locator, + cancellationToken); + resolvedQuestions.Add((question, reference)); } var oldItems = await dbContext.QuestionCollectionItems @@ -374,17 +377,19 @@ public sealed class ContentManagementService(TikuDbContext dbContext) : IContent .ToArrayAsync(cancellationToken); dbContext.QuestionCollectionItems.RemoveRange(oldItems); - var items = command.Questions - .Select((question, index) => new QuestionCollectionItem + var items = resolvedQuestions + .Select((resolved, index) => new QuestionCollectionItem { TenantId = actor.TenantId, CollectionId = command.CollectionId, - QuestionId = question.QuestionId, - SectionKey = Normalize(question.SectionKey), - SortOrder = question.Order ?? index, - Score = question.Score, - Required = question.Required ?? true, - Metadata = JsonObjectOrDefault(question.Metadata) + QuestionReferenceId = resolved.Reference.Id, + QuestionOwnerTenantId = resolved.Reference.QuestionOwnerTenantId, + QuestionId = resolved.Reference.QuestionId, + SectionKey = Normalize(resolved.Command.SectionKey), + SortOrder = resolved.Command.Order ?? index, + Score = resolved.Command.Score, + Required = resolved.Command.Required ?? true, + Metadata = JsonObjectOrDefault(resolved.Command.Metadata) }) .ToArray(); @@ -743,6 +748,9 @@ public sealed class ContentManagementService(TikuDbContext dbContext) : IContent item.Id, item.CollectionId, item.QuestionId, + new QuestionLocator( + item.QuestionOwnerTenantId == item.TenantId ? QuestionSource.Tenant : QuestionSource.Platform, + item.QuestionId), item.SectionKey, item.SortOrder, item.Score, diff --git a/Tiku.Infrastructure/Content/DirectContentService.cs b/Tiku.Infrastructure/Content/DirectContentService.cs index 458c62b..c7476eb 100644 --- a/Tiku.Infrastructure/Content/DirectContentService.cs +++ b/Tiku.Infrastructure/Content/DirectContentService.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore; using Tiku.Application.Assets; using Tiku.Application.Catalog; using Tiku.Application.Content; +using Tiku.Application.QuestionBanks; using Tiku.Domain.Catalog; using Tiku.Domain.Common; using Tiku.Domain.Content; @@ -14,7 +15,9 @@ using Tiku.Infrastructure.Persistence; namespace Tiku.Infrastructure.Content; -public sealed class DirectContentService(TikuDbContext dbContext) : IDirectContentService +public sealed class DirectContentService( + TikuDbContext dbContext, + IQuestionReferenceService questionReferenceService) : IDirectContentService { private const int DefaultLimit = 100; private const int MaxLimit = 1000; @@ -1424,6 +1427,11 @@ public sealed class DirectContentService(TikuDbContext dbContext) : IDirectConte cancellationToken); if (existing is null) { + var reference = await questionReferenceService.ResolveAsync( + actor.TenantId, + actor.UserId, + new QuestionLocator(QuestionSource.Tenant, question.Id), + cancellationToken); var nextOrder = await dbContext.QuestionCollectionItems .Where(item => item.TenantId == actor.TenantId && item.CollectionId == question.PrimaryCollectionId.Value) .Select(item => (int?)item.SortOrder) @@ -1432,6 +1440,8 @@ public sealed class DirectContentService(TikuDbContext dbContext) : IDirectConte { TenantId = actor.TenantId, CollectionId = question.PrimaryCollectionId.Value, + QuestionReferenceId = reference.Id, + QuestionOwnerTenantId = reference.QuestionOwnerTenantId, QuestionId = question.Id, SortOrder = nextOrder + 1 }); diff --git a/Tiku.Infrastructure/DependencyInjection.cs b/Tiku.Infrastructure/DependencyInjection.cs index f2fa50e..00ef287 100644 --- a/Tiku.Infrastructure/DependencyInjection.cs +++ b/Tiku.Infrastructure/DependencyInjection.cs @@ -15,6 +15,8 @@ using Tiku.Application.Scoreline; using Tiku.Application.Storage; using Tiku.Application.StudyContent; using Tiku.Application.TenantAdmin; +using Tiku.Application.Security; +using Tiku.Application.Tenancy; using Tiku.Infrastructure.Assets; using Tiku.Infrastructure.Auth; using Tiku.Infrastructure.Catalog; @@ -30,6 +32,7 @@ using Tiku.Infrastructure.Scoreline; using Tiku.Infrastructure.Storage; using Tiku.Infrastructure.StudyContent; using Tiku.Infrastructure.TenantAdmin; +using Tiku.Infrastructure.Tenancy; namespace Tiku.Infrastructure; @@ -42,12 +45,23 @@ public static class DependencyInjection ArgumentException.ThrowIfNullOrWhiteSpace(connectionString); services.AddSingleton(_ => NpgsqlDataSource.Create(connectionString)); - services.AddDbContextPool((serviceProvider, options) => + services.AddScoped(); + services.AddDbContext((serviceProvider, options) => { var dataSource = serviceProvider.GetRequiredService(); options.UseNpgsql(dataSource, npgsql => npgsql.MigrationsAssembly(typeof(TikuDbContext).Assembly.FullName)); + options.AddInterceptors(serviceProvider.GetRequiredService()); }); + services.AddScoped(); + services.AddMemoryCache(); + services.AddScoped(); + services.AddSingleton(); + services.AddHttpClient(); + services.AddHttpClient(); + services.AddScoped(); + services.AddOptions(); + services.AddSingleton(); services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -55,10 +69,13 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/Tiku.Infrastructure/Learning/LearningActivityService.cs b/Tiku.Infrastructure/Learning/LearningActivityService.cs index 49f4a68..cb0ad2d 100644 --- a/Tiku.Infrastructure/Learning/LearningActivityService.cs +++ b/Tiku.Infrastructure/Learning/LearningActivityService.cs @@ -1,6 +1,9 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; using System.Text.Json; using Tiku.Application.Learning; +using Tiku.Application.QuestionBanks; +using Tiku.Application.Security; using Tiku.Domain.Common; using Tiku.Domain.Content; using Tiku.Domain.Learning; @@ -9,7 +12,11 @@ using Tiku.Infrastructure.Persistence; namespace Tiku.Infrastructure.Learning; -public sealed class LearningActivityService(TikuDbContext dbContext) : ILearningActivityService +public sealed class LearningActivityService( + TikuDbContext dbContext, + IQuestionReferenceService questionReferenceService, + IPublicQuestionAccessPolicy publicQuestionAccessPolicy, + ITenantExecutionScope tenantExecutionScope) : ILearningActivityService { private const int DefaultLimit = 100; private const int MaxLimit = 500; @@ -113,47 +120,36 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning SubmitAnswerCommand command, CancellationToken cancellationToken = default) { - var question = await dbContext.Questions + var now = DateTimeOffset.UtcNow; + var sessionQuestion = await dbContext.PracticeSessionQuestions .AsNoTracking() .Where(item => item.TenantId == actor.TenantId && - item.Id == command.QuestionId && - item.Status == QuestionStatus.Published) - .Select(item => new - { - item.Id, - item.CurrentVersionId - }) - .SingleOrDefaultAsync(cancellationToken); - - if (question is null) - { - throw new LearningResourceNotFoundException("question_not_found", "Question was not found."); - } - - if (command.PracticeSessionId.HasValue) - { - var sessionExists = await dbContext.PracticeSessions.AnyAsync( - session => + item.Id == command.SessionQuestionId) + .Join( + dbContext.PracticeSessions.AsNoTracking().Where(session => session.TenantId == actor.TenantId && session.UserId == actor.UserId && - session.Id == command.PracticeSessionId.Value, - cancellationToken); + session.FinishedAt == null && + (!session.ExpiresAt.HasValue || session.ExpiresAt > now)), + item => new { item.TenantId, Id = item.PracticeSessionId }, + session => new { session.TenantId, session.Id }, + (item, session) => item) + .SingleOrDefaultAsync(cancellationToken); - if (!sessionExists) - { - throw new LearningResourceNotFoundException("practice_session_not_found", "Practice session was not found."); - } + if (sessionQuestion is null) + { + throw new LearningResourceNotFoundException( + "session_question_not_found", + "An active practice session question was not found."); } - var now = DateTimeOffset.UtcNow; var record = new AnswerRecord { TenantId = actor.TenantId, UserId = actor.UserId, - QuestionId = question.Id, - QuestionVersionId = question.CurrentVersionId, - PracticeSessionId = command.PracticeSessionId, + PracticeSessionId = sessionQuestion.PracticeSessionId, + SessionQuestionId = sessionQuestion.Id, SelectedOptions = JsonSerializer.SerializeToElement(command.SelectedOptions ?? []), AnswerText = command.AnswerText, IsCorrect = command.SelfJudgedCorrect, @@ -165,7 +161,7 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning if (command.SelfJudgedCorrect == false) { var wrongQuestion = await dbContext.WrongQuestions.FindAsync( - [actor.TenantId, actor.UserId, question.Id], + [actor.TenantId, actor.UserId, sessionQuestion.QuestionReferenceId], cancellationToken); if (wrongQuestion is null) @@ -174,7 +170,9 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning { TenantId = actor.TenantId, UserId = actor.UserId, - QuestionId = question.Id, + QuestionReferenceId = sessionQuestion.QuestionReferenceId, + QuestionOwnerTenantId = sessionQuestion.QuestionOwnerTenantId, + QuestionId = sessionQuestion.QuestionId, WrongCount = 1, LastWrongAt = now }); @@ -204,8 +202,10 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning .OrderByDescending(item => item.CreatedAt) .Take(ResolveLimit(filter.Limit)) .Select(item => new FavoriteQuestionItem( - item.QuestionId, - item.Source, + item.QuestionReferenceId, + new QuestionLocator( + item.QuestionOwnerTenantId == item.TenantId ? QuestionSource.Tenant : QuestionSource.Platform, + item.QuestionId), item.CreatedAt)) .ToArrayAsync(cancellationToken); @@ -217,11 +217,15 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning QuestionActionCommand command, CancellationToken cancellationToken = default) { - await EnsureQuestionExistsAsync(actor.TenantId, command.QuestionId, cancellationToken); + var reference = await questionReferenceService.ResolveAsync( + actor.TenantId, + actor.UserId, + command.Locator, + cancellationToken); var favorite = command.Favorite ?? true; var item = await dbContext.FavoriteQuestions.FindAsync( - [actor.TenantId, actor.UserId, command.QuestionId], + [actor.TenantId, actor.UserId, reference.Id], cancellationToken); if (favorite) @@ -232,8 +236,10 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning { TenantId = actor.TenantId, UserId = actor.UserId, - QuestionId = command.QuestionId, - Source = "api", + QuestionReferenceId = reference.Id, + QuestionOwnerTenantId = reference.QuestionOwnerTenantId, + QuestionId = reference.QuestionId, + Source = reference.Source.ToString().ToLowerInvariant(), CreatedAt = DateTimeOffset.UtcNow }); } @@ -267,7 +273,10 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning .OrderByDescending(item => item.LastWrongAt) .Take(ResolveLimit(filter.Limit)) .Select(item => new WrongQuestionItem( - item.QuestionId, + item.QuestionReferenceId, + new QuestionLocator( + item.QuestionOwnerTenantId == item.TenantId ? QuestionSource.Tenant : QuestionSource.Platform, + item.QuestionId), item.WrongCount, item.LastWrongAt, item.ResolvedAt)) @@ -281,8 +290,13 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning QuestionActionCommand command, CancellationToken cancellationToken = default) { + var reference = await questionReferenceService.ResolveAsync( + actor.TenantId, + actor.UserId, + command.Locator, + cancellationToken); var item = await dbContext.WrongQuestions.FindAsync( - [actor.TenantId, actor.UserId, command.QuestionId], + [actor.TenantId, actor.UserId, reference.Id], cancellationToken); if (item is null) @@ -308,7 +322,13 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning .OrderByDescending(item => item.WrongCount) .ThenBy(item => item.LastWrongAt) .Take(ResolveLimit(filter.Limit)) - .Select(item => new WrongQuestionReviewPlanItem(item.QuestionId, item.WrongCount, item.LastWrongAt)) + .Select(item => new WrongQuestionReviewPlanItem( + item.QuestionReferenceId, + new QuestionLocator( + item.QuestionOwnerTenantId == item.TenantId ? QuestionSource.Tenant : QuestionSource.Platform, + item.QuestionId), + item.WrongCount, + item.LastWrongAt)) .ToArrayAsync(cancellationToken); return new WrongQuestionReviewPlan( items, @@ -594,12 +614,24 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning CancellationToken cancellationToken = default) { var assembly = await BuildPracticeAssemblyAsync(actor.TenantId, command, cancellationToken); - var questionIds = await CollectQuestionIdsAsync(actor, assembly, cancellationToken); - if (questionIds.Count == 0) + var questionReferenceIds = await CollectQuestionReferenceIdsAsync(actor, assembly, cancellationToken); + if (questionReferenceIds.Count == 0) { throw new LearningValidationException("no_practice_questions", "No published questions are available for this practice target."); } + + var containsPlatformQuestion = await dbContext.TenantQuestionReferences.AsNoTracking().AnyAsync( + reference => + reference.TenantId == actor.TenantId && + questionReferenceIds.Contains(reference.Id) && + reference.Source == QuestionSource.Platform, + cancellationToken); + if (containsPlatformQuestion) + { + await publicQuestionAccessPolicy.EnsureCanStartAsync(actor.TenantId, cancellationToken); + } + var now = DateTimeOffset.UtcNow; var session = new PracticeSession { @@ -612,26 +644,46 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning CollectionId = assembly.CollectionId, EntryId = assembly.EntryId, ContentNodeId = assembly.ContentNodeId, - QuestionIds = JsonSerializer.SerializeToElement(questionIds), - QuestionCount = questionIds.Count, + QuestionCount = questionReferenceIds.Count, DurationMinutes = assembly.DurationMinutes, TotalScore = assembly.TotalScore, ExpiresAt = assembly.DurationMinutes.HasValue ? now.AddMinutes(assembly.DurationMinutes.Value) : null, AccessMode = PracticeAccessMode.Free, - ConsumedFreeQuota = questionIds.Count, + ConsumedFreeQuota = questionReferenceIds.Count, AccessSnapshot = JsonSerializer.SerializeToElement(new { strategy = "v1_free", requestedCount = assembly.QuestionLimit, - grantedCount = questionIds.Count + grantedCount = questionReferenceIds.Count }), Metadata = command.Metadata.ValueKind is JsonValueKind.Undefined ? JsonDefaults.Object() : command.Metadata }; dbContext.PracticeSessions.Add(session); + + await dbContext.SaveChangesAsync(cancellationToken); + var selections = await LoadQuestionSelectionsAsync( + actor.TenantId, + questionReferenceIds, + cancellationToken); + var scorePerQuestion = session.TotalScore.HasValue && selections.Count > 0 + ? session.TotalScore.Value / selections.Count + : (decimal?)null; + dbContext.PracticeSessionQuestions.AddRange(selections.Select((selection, index) => + new PracticeSessionQuestion + { + TenantId = actor.TenantId, + PracticeSessionId = session.Id, + QuestionReferenceId = selection.QuestionReferenceId, + QuestionOwnerTenantId = selection.QuestionOwnerTenantId, + QuestionId = selection.QuestionId, + QuestionVersionId = selection.QuestionVersionId, + Position = index, + Score = scorePerQuestion + })); dbContext.PracticeAccessEvents.Add(new PracticeAccessEvent { TenantId = actor.TenantId, @@ -640,8 +692,8 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning EventType = PracticeAccessEventType.SessionCreated, AccessMode = PracticeAccessEventMode.Free, RequestedCount = assembly.QuestionLimit, - GrantedCount = questionIds.Count, - ConsumedFreeQuota = questionIds.Count, + GrantedCount = selections.Count, + ConsumedFreeQuota = selections.Count, Metadata = session.AccessSnapshot }); @@ -655,44 +707,20 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning CancellationToken cancellationToken = default) { var session = await GetPracticeSessionAsync(actor, filter.PracticeSessionId, cancellationToken); - var questionIds = ReadGuidArray(session.QuestionIds); - var questions = await dbContext.Questions - .AsNoTracking() - .Where(question => - question.TenantId == actor.TenantId && - questionIds.Contains(question.Id)) - .GroupJoin( - dbContext.QuestionVersions.AsNoTracking(), - question => new { question.TenantId, QuestionId = question.Id, VersionId = question.CurrentVersionId }, - version => new { version.TenantId, version.QuestionId, VersionId = (Guid?)version.Id }, - (question, versions) => new { question, version = versions.FirstOrDefault() }) - .Select(row => new PracticeSessionQuestionItem( - row.question.Id, - row.question.Type, - row.question.TypeLabel, - row.question.Difficulty, - row.question.Tags, - row.version == null ? null : row.version.Id, - row.version == null ? null : row.version.Content, - row.version == null ? JsonDefaults.Array() : row.version.Options, - row.version == null ? null : row.version.Explanation)) - .ToArrayAsync(cancellationToken); - var questionById = questions.ToDictionary(question => question.Id); - var orderedQuestions = questionIds - .Where(questionById.ContainsKey) - .Select(questionId => questionById[questionId]) - .ToArray(); + var orderedQuestions = await LoadSessionQuestionItemsAsync( + actor.TenantId, + session.Id, + cancellationToken); var answers = await dbContext.AnswerRecords .AsNoTracking() .Where(answer => answer.TenantId == actor.TenantId && answer.UserId == actor.UserId && - answer.PracticeSessionId == session.Id && - answer.QuestionId != null) + answer.PracticeSessionId == session.Id) .ToArrayAsync(cancellationToken); var answersByQuestion = answers - .GroupBy(answer => answer.QuestionId!.Value) + .GroupBy(answer => answer.SessionQuestionId) .ToDictionary( group => group.Key, group => ToItem(group.OrderByDescending(answer => answer.AnsweredAt).First())); @@ -885,7 +913,7 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning }; } - private async Task> CollectQuestionIdsAsync( + private async Task> CollectQuestionReferenceIdsAsync( LearningActor actor, PracticeAssembly assembly, CancellationToken cancellationToken) @@ -898,16 +926,10 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning item.TenantId == actor.TenantId && item.UserId == actor.UserId && item.ResolvedAt == null) - .Join( - dbContext.Questions.AsNoTracking(), - item => new { item.TenantId, item.QuestionId }, - question => new { question.TenantId, QuestionId = question.Id }, - (item, question) => new { item, question }) - .Where(row => row.question.Status == QuestionStatus.Published) - .OrderByDescending(row => row.item.WrongCount) - .ThenBy(row => row.item.LastWrongAt) + .OrderByDescending(item => item.WrongCount) + .ThenBy(item => item.LastWrongAt) .Take(assembly.QuestionLimit) - .Select(row => row.question.Id) + .Select(item => item.QuestionReferenceId) .ToListAsync(cancellationToken); } @@ -918,15 +940,9 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning .Where(item => item.TenantId == actor.TenantId && item.UserId == actor.UserId) - .Join( - dbContext.Questions.AsNoTracking(), - item => new { item.TenantId, item.QuestionId }, - question => new { question.TenantId, QuestionId = question.Id }, - (item, question) => new { item, question }) - .Where(row => row.question.Status == QuestionStatus.Published) - .OrderByDescending(row => row.item.CreatedAt) + .OrderByDescending(item => item.CreatedAt) .Take(assembly.QuestionLimit) - .Select(row => row.question.Id) + .Select(item => item.QuestionReferenceId) .ToListAsync(cancellationToken); } @@ -937,16 +953,9 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning .Where(item => item.TenantId == actor.TenantId && item.CollectionId == assembly.CollectionId.Value) - .Join( - dbContext.Questions.AsNoTracking(), - item => new { item.TenantId, item.QuestionId }, - question => new { question.TenantId, QuestionId = question.Id }, - (item, question) => new { item, question }) - .Where(row => row.question.Status == QuestionStatus.Published) - .OrderBy(row => row.item.SortOrder) - .ThenBy(row => row.question.CreatedAt) + .OrderBy(item => item.SortOrder) .Take(assembly.QuestionLimit) - .Select(row => row.question.Id) + .Select(item => item.QuestionReferenceId) .ToListAsync(cancellationToken); } @@ -973,11 +982,23 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning throw new LearningValidationException("practice_target_required", "Practice target is required."); } - return await query + var questionIds = await query .OrderBy(question => question.CreatedAt) .Take(assembly.QuestionLimit) .Select(question => question.Id) .ToListAsync(cancellationToken); + var referenceIds = new List(questionIds.Count); + foreach (var questionId in questionIds) + { + var reference = await questionReferenceService.ResolveAsync( + actor.TenantId, + actor.UserId, + new QuestionLocator(QuestionSource.Tenant, questionId), + cancellationToken); + referenceIds.Add(reference.Id); + } + + return referenceIds; } private static IQueryable ApplyLegacyTargetFilter( @@ -997,6 +1018,111 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning }; } + private async Task> LoadQuestionSelectionsAsync( + Guid tenantId, + IReadOnlyCollection questionReferenceIds, + CancellationToken cancellationToken) + { + var rows = await tenantExecutionScope.ExecuteAsync( + tenantId, + "Lock published question versions for a new practice session", + async (provider, token) => + { + var systemDbContext = provider.GetRequiredService(); + return await ( + from reference in systemDbContext.TenantQuestionReferences.AsNoTracking() + join question in systemDbContext.Questions.AsNoTracking() + on new { TenantId = reference.QuestionOwnerTenantId, Id = reference.QuestionId } + equals new { question.TenantId, question.Id } + join version in systemDbContext.QuestionVersions.AsNoTracking() + on new + { + TenantId = reference.QuestionOwnerTenantId, + reference.QuestionId, + Id = question.CurrentVersionId + } + equals new + { + version.TenantId, + version.QuestionId, + Id = (Guid?)version.Id + } + where reference.TenantId == tenantId && + questionReferenceIds.Contains(reference.Id) && + question.Status == QuestionStatus.Published + select new QuestionSelection( + reference.Id, + reference.QuestionOwnerTenantId, + reference.QuestionId, + version.Id)) + .ToArrayAsync(token); + }, + cancellationToken); + + var byReference = rows.ToDictionary(row => row.QuestionReferenceId); + if (byReference.Count != questionReferenceIds.Distinct().Count()) + { + throw new LearningValidationException( + "practice_question_unavailable", + "One or more practice questions have no published version."); + } + + return questionReferenceIds.Select(referenceId => byReference[referenceId]).ToArray(); + } + + private Task LoadSessionQuestionItemsAsync( + Guid tenantId, + Guid practiceSessionId, + CancellationToken cancellationToken) + { + return tenantExecutionScope.ExecuteAsync( + tenantId, + "Read locked question versions for a tenant practice session", + async (provider, token) => + { + var systemDbContext = provider.GetRequiredService(); + return await ( + from sessionQuestion in systemDbContext.PracticeSessionQuestions.AsNoTracking() + join question in systemDbContext.Questions.AsNoTracking() + on new + { + TenantId = sessionQuestion.QuestionOwnerTenantId, + Id = sessionQuestion.QuestionId + } + equals new { question.TenantId, question.Id } + join version in systemDbContext.QuestionVersions.AsNoTracking() + on new + { + TenantId = sessionQuestion.QuestionOwnerTenantId, + sessionQuestion.QuestionId, + Id = sessionQuestion.QuestionVersionId + } + equals new { version.TenantId, version.QuestionId, version.Id } + where sessionQuestion.TenantId == tenantId && + sessionQuestion.PracticeSessionId == practiceSessionId + orderby sessionQuestion.Position + select new PracticeSessionQuestionItem( + sessionQuestion.Id, + sessionQuestion.QuestionReferenceId, + new QuestionLocator( + sessionQuestion.QuestionOwnerTenantId == tenantId + ? QuestionSource.Tenant + : QuestionSource.Platform, + sessionQuestion.QuestionId), + question.Id, + question.Type, + question.TypeLabel, + question.Difficulty, + question.Tags, + version.Id, + version.Content, + version.Options, + version.Explanation)) + .ToArrayAsync(token); + }, + cancellationToken); + } + private async Task GetPracticeSessionAsync( LearningActor actor, Guid? practiceSessionId, @@ -1028,8 +1154,13 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning PracticeSession session, CancellationToken cancellationToken) { - var questionIds = ReadGuidArray(session.QuestionIds); - if (questionIds.Count == 0) + var sessionQuestions = await dbContext.PracticeSessionQuestions.AsNoTracking() + .Where(item => + item.TenantId == actor.TenantId && + item.PracticeSessionId == session.Id) + .OrderBy(item => item.Position) + .ToArrayAsync(cancellationToken); + if (sessionQuestions.Length == 0) { throw new LearningValidationException("practice_session_empty", "Practice session has no question snapshot."); } @@ -1039,21 +1170,20 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning .Where(answer => answer.TenantId == actor.TenantId && answer.UserId == actor.UserId && - answer.PracticeSessionId == session.Id && - answer.QuestionId != null) + answer.PracticeSessionId == session.Id) .ToArrayAsync(cancellationToken); var latestAnswers = answers - .GroupBy(answer => answer.QuestionId!.Value) + .GroupBy(answer => answer.SessionQuestionId) .ToDictionary( group => group.Key, group => group.OrderByDescending(answer => answer.AnsweredAt).First()); - var totalQuestions = questionIds.Count; - var answeredCount = questionIds.Count(latestAnswers.ContainsKey); - var correctCount = questionIds.Count(questionId => - latestAnswers.TryGetValue(questionId, out var answer) && + var totalQuestions = sessionQuestions.Length; + var answeredCount = sessionQuestions.Count(question => latestAnswers.ContainsKey(question.Id)); + var correctCount = sessionQuestions.Count(question => + latestAnswers.TryGetValue(question.Id, out var answer) && answer.IsCorrect == true); - var wrongCount = questionIds.Count(questionId => - latestAnswers.TryGetValue(questionId, out var answer) && + var wrongCount = sessionQuestions.Count(question => + latestAnswers.TryGetValue(question.Id, out var answer) && answer.IsCorrect != true); var unansweredCount = Math.Max(0, totalQuestions - answeredCount); var totalScore = session.TotalScore ?? totalQuestions; @@ -1062,18 +1192,22 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning var accuracy = totalQuestions == 0 ? 0 : Math.Round((decimal)correctCount / totalQuestions, 4); var submittedAt = DateTimeOffset.UtcNow; var durationSeconds = Math.Max(0, (int)(submittedAt - session.StartedAt).TotalSeconds); - var wrongQuestionIds = questionIds - .Where(questionId => - latestAnswers.TryGetValue(questionId, out var answer) && + var wrongQuestionIds = sessionQuestions + .Where(question => + latestAnswers.TryGetValue(question.Id, out var answer) && answer.IsCorrect != true) + .Select(question => question.QuestionReferenceId) .ToArray(); - var questionResults = questionIds - .Select(questionId => + var questionResults = sessionQuestions + .Select(question => { - latestAnswers.TryGetValue(questionId, out var answer); + latestAnswers.TryGetValue(question.Id, out var answer); return new { - questionId, + sessionQuestionId = question.Id, + questionReferenceId = question.QuestionReferenceId, + questionId = question.QuestionId, + source = question.QuestionOwnerTenantId == actor.TenantId ? "tenant" : "platform", answered = answer is not null, isCorrect = answer?.IsCorrect, score = answer?.IsCorrect == true ? scorePerQuestion : 0, @@ -1190,8 +1324,7 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning { return new AnswerRecordItem( record.Id, - record.QuestionId!.Value, - record.QuestionVersionId, + record.SessionQuestionId, record.PracticeSessionId, record.SelectedOptions, record.AnswerText, @@ -1226,7 +1359,6 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning item.CollectionId, item.EntryId, item.ContentNodeId, - item.QuestionIds, item.QuestionCount, item.DurationMinutes, item.TotalScore, @@ -1340,6 +1472,12 @@ public sealed class LearningActivityService(TikuDbContext dbContext) : ILearning int QuestionLimit, int? DurationMinutes, decimal? TotalScore); + + private sealed record QuestionSelection( + Guid QuestionReferenceId, + Guid QuestionOwnerTenantId, + Guid QuestionId, + Guid QuestionVersionId); } public class LearningException(string code, string message) : Exception(message) diff --git a/Tiku.Infrastructure/Persistence/Configurations/AssetConfigurations.cs b/Tiku.Infrastructure/Persistence/Configurations/AssetConfigurations.cs index 4a89477..2413cd5 100644 --- a/Tiku.Infrastructure/Persistence/Configurations/AssetConfigurations.cs +++ b/Tiku.Infrastructure/Persistence/Configurations/AssetConfigurations.cs @@ -182,7 +182,7 @@ internal sealed class ContentImportItemConfiguration : IEntityTypeConfiguration< builder.Property(entity => entity.SourcePayload).IsJson("{}"); builder.Property(entity => entity.NormalizedPayload).IsJson("{}"); builder.Property(entity => entity.ContentHash).HasMaxLength(128); - builder.HasIndex(entity => new { entity.JobId, entity.RowNo }).IsUnique(); + builder.HasIndex(entity => new { entity.TenantId, entity.JobId, entity.RowNo }).IsUnique(); builder.HasIndex(entity => new { entity.TenantId, entity.JobId, entity.Status, entity.RowNo }); builder.ToTable(table => { diff --git a/Tiku.Infrastructure/Persistence/Configurations/CommerceConfigurations.cs b/Tiku.Infrastructure/Persistence/Configurations/CommerceConfigurations.cs index 7071805..299b1b0 100644 --- a/Tiku.Infrastructure/Persistence/Configurations/CommerceConfigurations.cs +++ b/Tiku.Infrastructure/Persistence/Configurations/CommerceConfigurations.cs @@ -134,7 +134,7 @@ internal sealed class PaymentConfiguration : IEntityTypeConfiguration builder.Property(entity => entity.RawPayload).IsJson("{}"); builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique(); builder.HasIndex(entity => new { entity.TenantId, entity.OrderId, entity.UpdatedAt }); - builder.HasIndex(entity => new { entity.Provider, entity.ProviderTradeNo }) + builder.HasIndex(entity => new { entity.TenantId, entity.Provider, entity.ProviderTradeNo }) .IsUnique() .HasFilter("provider_trade_no is not null"); builder.ToTable(table => @@ -160,7 +160,7 @@ internal sealed class PaymentEventConfiguration : IEntityTypeConfiguration entity.EventId).HasMaxLength(200); builder.Property(entity => entity.Payload).IsJson("{}"); builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()"); - builder.HasIndex(entity => new { entity.Provider, entity.EventId }).IsUnique(); + builder.HasIndex(entity => new { entity.TenantId, entity.Provider, entity.EventId }).IsUnique(); builder.HasOne().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, entity.PaymentId }) @@ -438,7 +438,7 @@ internal sealed class CommerceReconciliationItemConfiguration : IEntityTypeConfi builder.Property(entity => entity.IssueCode).HasMaxLength(100); builder.Property(entity => entity.Details).IsJson("{}"); builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()"); - builder.HasIndex(entity => new { entity.BatchId, entity.RowNo }).IsUnique(); + builder.HasIndex(entity => new { entity.TenantId, entity.BatchId, entity.RowNo }).IsUnique(); builder.HasIndex(entity => new { entity.TenantId, entity.BatchId, entity.MatchStatus, entity.RowNo }); builder.HasIndex(entity => new { entity.TenantId, entity.OrderNo, entity.CreatedAt }); builder.HasIndex(entity => new { entity.TenantId, entity.RefundNo, entity.ProviderRefundNo, entity.CreatedAt }); diff --git a/Tiku.Infrastructure/Persistence/Configurations/ContentConfigurations.cs b/Tiku.Infrastructure/Persistence/Configurations/ContentConfigurations.cs index 9776cc2..1e0adae 100644 --- a/Tiku.Infrastructure/Persistence/Configurations/ContentConfigurations.cs +++ b/Tiku.Infrastructure/Persistence/Configurations/ContentConfigurations.cs @@ -180,7 +180,7 @@ internal sealed class QuestionCollectionItemConfiguration : { entity.TenantId, entity.CollectionId, - entity.QuestionId + entity.QuestionReferenceId }).IsUnique(); builder.HasIndex(entity => new { @@ -194,10 +194,14 @@ internal sealed class QuestionCollectionItemConfiguration : .HasForeignKey(entity => new { entity.TenantId, entity.CollectionId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Cascade); - builder.HasOne().WithMany() - .HasForeignKey(entity => new { entity.TenantId, entity.QuestionId }) + builder.HasOne().WithMany() + .HasForeignKey(entity => new { entity.TenantId, entity.QuestionReferenceId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Cascade); + builder.HasOne().WithMany() + .HasForeignKey(entity => new { entity.QuestionOwnerTenantId, entity.QuestionId }) + .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) + .OnDelete(DeleteBehavior.Restrict); } } diff --git a/Tiku.Infrastructure/Persistence/Configurations/ContentPlatformConfigurations.cs b/Tiku.Infrastructure/Persistence/Configurations/ContentPlatformConfigurations.cs index f23ca2f..449ada3 100644 --- a/Tiku.Infrastructure/Persistence/Configurations/ContentPlatformConfigurations.cs +++ b/Tiku.Infrastructure/Persistence/Configurations/ContentPlatformConfigurations.cs @@ -65,72 +65,48 @@ internal sealed class ContentAssetSecurityScanEventConfiguration : IEntityTypeCo } } -internal sealed class QuestionBankGrantConfiguration : IEntityTypeConfiguration +internal sealed class TenantQuestionBankPreferenceConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) + public void Configure(EntityTypeBuilder builder) { - builder.ConfigureEntity("question_bank_grants"); + builder.ConfigureTenantEntity("tenant_question_bank_preferences"); builder.ConfigureTimestamps(); - builder.Property(entity => entity.GrantScope).HasSnakeCaseEnum(); - builder.Property(entity => entity.AllowedPlanCodes) - .HasColumnType("text[]") - .HasDefaultValueSql("'{}'::text[]"); - builder.Property(entity => entity.AllowedTenantIds) - .HasColumnType("uuid[]") - .HasDefaultValueSql("'{}'::uuid[]"); - builder.Property(entity => entity.AllowedRegionIds) - .HasColumnType("uuid[]") - .HasDefaultValueSql("'{}'::uuid[]"); - builder.Property(entity => entity.AllowedSubjectIds) - .HasColumnType("uuid[]") - .HasDefaultValueSql("'{}'::uuid[]"); - builder.Property(entity => entity.Status).HasSnakeCaseEnum(); + builder.Property(entity => entity.Alias).HasMaxLength(300); + builder.Property(entity => entity.NavigationLocation).HasMaxLength(100); builder.Property(entity => entity.Metadata).IsJson("{}"); - builder.HasIndex(entity => new { entity.SourceQuestionBankId, entity.Status, entity.StartsAt, entity.ExpiresAt }); - builder.HasIndex(entity => entity.AllowedTenantIds).HasMethod("gin"); - builder.HasIndex(entity => entity.AllowedPlanCodes).HasMethod("gin"); + builder.HasIndex(entity => new + { + entity.TenantId, + entity.QuestionBankOwnerTenantId, + entity.QuestionBankId + }).IsUnique(); builder.HasOne().WithMany() - .HasForeignKey(entity => entity.SourceQuestionBankId) - .OnDelete(DeleteBehavior.Cascade); + .HasForeignKey(entity => new { entity.QuestionBankOwnerTenantId, entity.QuestionBankId }) + .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) + .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany().HasForeignKey(entity => entity.CreatedBy).OnDelete(DeleteBehavior.SetNull); builder.HasOne().WithMany().HasForeignKey(entity => entity.UpdatedBy).OnDelete(DeleteBehavior.SetNull); } } -internal sealed class TenantQuestionBankAdoptionConfiguration : IEntityTypeConfiguration +internal sealed class TenantQuestionReferenceConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) + public void Configure(EntityTypeBuilder builder) { - builder.ConfigureTenantEntity("tenant_question_bank_adoptions"); + builder.ConfigureTenantEntity("tenant_question_references"); builder.ConfigureTimestamps(); - builder.Property(entity => entity.AdoptionMode).HasSnakeCaseEnum(); - builder.Property(entity => entity.Status).HasSnakeCaseEnum(); - builder.Property(entity => entity.SyncStatus).HasSnakeCaseEnum(); - builder.Property(entity => entity.SourceSnapshot).IsJson("{}"); - builder.Property(entity => entity.Metadata).IsJson("{}"); - builder.HasIndex(entity => new { entity.TenantId, entity.SourceQuestionBankId }).IsUnique(); - builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.UpdatedAt }); - builder.ToTable(table => table.HasCheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0")); - builder.HasOne().WithMany() - .HasForeignKey(entity => entity.SourceQuestionBankId) - .OnDelete(DeleteBehavior.Cascade); - builder.HasOne().WithMany() - .HasForeignKey(entity => entity.GrantId) - .OnDelete(DeleteBehavior.SetNull); - builder.HasOne().WithMany() - .HasForeignKey(entity => new { entity.TenantId, entity.TargetQuestionBankId }) - .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) - .OnDelete(DeleteBehavior.Restrict); - builder.HasOne().WithMany() - .HasForeignKey(entity => new { entity.TenantId, entity.TargetEntryId }) - .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) - .OnDelete(DeleteBehavior.Restrict); - builder.HasOne().WithMany() - .HasForeignKey(entity => new { entity.TenantId, entity.TargetCollectionId }) + builder.Property(entity => entity.Source).HasSnakeCaseEnum(); + builder.HasAlternateKey(entity => new + { + entity.TenantId, + entity.QuestionOwnerTenantId, + entity.QuestionId + }); + builder.HasOne().WithMany() + .HasForeignKey(entity => new { entity.QuestionOwnerTenantId, entity.QuestionId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany().HasForeignKey(entity => entity.CreatedBy).OnDelete(DeleteBehavior.SetNull); - builder.HasOne().WithMany().HasForeignKey(entity => entity.UpdatedBy).OnDelete(DeleteBehavior.SetNull); } } diff --git a/Tiku.Infrastructure/Persistence/Configurations/LearningConfigurations.cs b/Tiku.Infrastructure/Persistence/Configurations/LearningConfigurations.cs index aa9adb4..8ff5632 100644 --- a/Tiku.Infrastructure/Persistence/Configurations/LearningConfigurations.cs +++ b/Tiku.Infrastructure/Persistence/Configurations/LearningConfigurations.cs @@ -17,7 +17,6 @@ internal sealed class PracticeSessionConfiguration : IEntityTypeConfiguration entity.Mode).HasMaxLength(50); builder.Property(entity => entity.TargetType).HasMaxLength(50); builder.Property(entity => entity.StartedAt).HasDefaultValueSql("now()"); - builder.Property(entity => entity.QuestionIds).IsJson("[]"); builder.Property(entity => entity.TotalScore).HasPrecision(8, 2); builder.Property(entity => entity.AccessMode) .HasSnakeCaseEnum() @@ -55,6 +54,45 @@ internal sealed class PracticeSessionConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ConfigureTenantEntity("practice_session_questions"); + builder.HasAlternateKey(entity => new { entity.TenantId, entity.PracticeSessionId, entity.Id }); + builder.HasIndex(entity => new { entity.TenantId, entity.PracticeSessionId, entity.Position }).IsUnique(); + builder.Property(entity => entity.Score).HasPrecision(8, 2); + + builder.HasOne().WithMany() + .HasForeignKey(entity => new { entity.TenantId, entity.PracticeSessionId }) + .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) + .OnDelete(DeleteBehavior.Cascade); + builder.HasOne().WithMany() + .HasForeignKey(entity => new + { + entity.TenantId, + entity.QuestionOwnerTenantId, + entity.QuestionId + }) + .HasPrincipalKey(entity => new + { + entity.TenantId, + entity.QuestionOwnerTenantId, + entity.QuestionId + }) + .OnDelete(DeleteBehavior.Restrict); + builder.HasOne().WithMany() + .HasForeignKey(entity => new + { + TenantId = entity.QuestionOwnerTenantId, + entity.QuestionId, + Id = entity.QuestionVersionId + }) + .HasPrincipalKey(entity => new { entity.TenantId, entity.QuestionId, entity.Id }) + .OnDelete(DeleteBehavior.Restrict); + } +} + internal sealed class AnswerRecordConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) @@ -67,13 +105,6 @@ internal sealed class AnswerRecordConfiguration : IEntityTypeConfiguration entity.AnsweredAt).HasDefaultValueSql("now()"); builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()"); builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique(); - builder.HasIndex(entity => new { entity.TenantId, entity.QuestionId }); - builder.HasIndex(entity => new - { - entity.TenantId, - entity.QuestionId, - entity.QuestionVersionId - }); builder.HasIndex(entity => new { entity.TenantId, @@ -81,31 +112,9 @@ internal sealed class AnswerRecordConfiguration : IEntityTypeConfiguration table.HasCheckConstraint( - "ck_answer_records_version_requires_question", - "question_version_id is null or question_id is not null")); - builder.HasOne().WithMany() .HasForeignKey(entity => entity.UserId) .OnDelete(DeleteBehavior.Cascade); - builder.HasOne().WithMany() - .HasForeignKey(entity => new { entity.TenantId, entity.QuestionId }) - .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) - .OnDelete(DeleteBehavior.Restrict); - builder.HasOne().WithMany() - .HasForeignKey(entity => new - { - entity.TenantId, - entity.QuestionId, - entity.QuestionVersionId - }) - .HasPrincipalKey(entity => new - { - entity.TenantId, - entity.QuestionId, - entity.Id - }) - .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany() .HasForeignKey(entity => new { @@ -120,6 +129,20 @@ internal sealed class AnswerRecordConfiguration : IEntityTypeConfiguration().WithMany() + .HasForeignKey(entity => new + { + entity.TenantId, + entity.PracticeSessionId, + Id = entity.SessionQuestionId + }) + .HasPrincipalKey(entity => new + { + entity.TenantId, + entity.PracticeSessionId, + entity.Id + }) + .OnDelete(DeleteBehavior.Restrict); } } @@ -128,7 +151,7 @@ internal sealed class FavoriteQuestionConfiguration : IEntityTypeConfiguration builder) { builder.ToTable("favorite_questions"); - builder.HasKey(entity => new { entity.TenantId, entity.UserId, entity.QuestionId }); + builder.HasKey(entity => new { entity.TenantId, entity.UserId, entity.QuestionReferenceId }); builder.Property(entity => entity.Source).HasMaxLength(50); builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()"); @@ -138,10 +161,14 @@ internal sealed class FavoriteQuestionConfiguration : IEntityTypeConfiguration().WithMany() .HasForeignKey(entity => entity.UserId) .OnDelete(DeleteBehavior.Cascade); - builder.HasOne().WithMany() - .HasForeignKey(entity => new { entity.TenantId, entity.QuestionId }) + builder.HasOne().WithMany() + .HasForeignKey(entity => new { entity.TenantId, entity.QuestionReferenceId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Cascade); + builder.HasOne().WithMany() + .HasForeignKey(entity => new { entity.QuestionOwnerTenantId, entity.QuestionId }) + .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) + .OnDelete(DeleteBehavior.Restrict); } } @@ -150,7 +177,7 @@ internal sealed class WrongQuestionConfiguration : IEntityTypeConfiguration builder) { builder.ToTable("wrong_questions"); - builder.HasKey(entity => new { entity.TenantId, entity.UserId, entity.QuestionId }); + builder.HasKey(entity => new { entity.TenantId, entity.UserId, entity.QuestionReferenceId }); builder.Property(entity => entity.WrongCount).HasDefaultValue(1); builder.Property(entity => entity.LastWrongAt).HasDefaultValueSql("now()"); @@ -160,10 +187,14 @@ internal sealed class WrongQuestionConfiguration : IEntityTypeConfiguration().WithMany() .HasForeignKey(entity => entity.UserId) .OnDelete(DeleteBehavior.Cascade); - builder.HasOne().WithMany() - .HasForeignKey(entity => new { entity.TenantId, entity.QuestionId }) + builder.HasOne().WithMany() + .HasForeignKey(entity => new { entity.TenantId, entity.QuestionReferenceId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Cascade); + builder.HasOne().WithMany() + .HasForeignKey(entity => new { entity.QuestionOwnerTenantId, entity.QuestionId }) + .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) + .OnDelete(DeleteBehavior.Restrict); } } diff --git a/Tiku.Infrastructure/Persistence/Configurations/PlatformOperationsConfigurations.cs b/Tiku.Infrastructure/Persistence/Configurations/PlatformOperationsConfigurations.cs index d5185d1..7b7df36 100644 --- a/Tiku.Infrastructure/Persistence/Configurations/PlatformOperationsConfigurations.cs +++ b/Tiku.Infrastructure/Persistence/Configurations/PlatformOperationsConfigurations.cs @@ -59,7 +59,7 @@ internal sealed class TenantInvoiceConfiguration : IEntityTypeConfiguration entity.Status).HasSnakeCaseEnum(); builder.Property(entity => entity.Currency).HasMaxLength(10); builder.Property(entity => entity.Metadata).IsJson("{}"); - builder.HasIndex(entity => entity.InvoiceNo).IsUnique(); + builder.HasIndex(entity => new { entity.TenantId, entity.InvoiceNo }).IsUnique(); builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.DueDate }); builder.HasIndex(entity => new { entity.TenantId, entity.BillingPeriodStart, entity.BillingPeriodEnd }) .IsUnique() @@ -110,7 +110,7 @@ internal sealed class TenantInvoicePaymentConfiguration : IEntityTypeConfigurati builder.Property(entity => entity.Status).HasSnakeCaseEnum(); builder.Property(entity => entity.ProviderTradeNo).HasMaxLength(200); builder.Property(entity => entity.RawPayload).IsJson("{}"); - builder.HasIndex(entity => entity.PaymentNo).IsUnique(); + builder.HasIndex(entity => new { entity.TenantId, entity.PaymentNo }).IsUnique(); builder.HasIndex(entity => new { entity.InvoiceId, entity.Status }); builder.ToTable(table => table.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0")); builder.HasOne().WithMany() @@ -229,7 +229,7 @@ internal sealed class PlatformDunningNotificationEventConfiguration : IEntityTyp builder.Property(entity => entity.RequestPayload).IsJson("{}"); builder.Property(entity => entity.Metadata).IsJson("{}"); builder.Property(entity => entity.ScheduledAt).HasDefaultValueSql("now()"); - builder.HasIndex(entity => new { entity.ChannelId, entity.ReminderId }).IsUnique(); + builder.HasIndex(entity => new { entity.TenantId, entity.ChannelId, entity.ReminderId }).IsUnique(); builder.HasIndex(entity => new { entity.Status, entity.NextAttemptAt, entity.ScheduledAt, entity.CreatedAt }); builder.HasIndex(entity => new { entity.ReminderId, entity.Status, entity.CreatedAt }); builder.HasIndex(entity => new { entity.InvoiceId, entity.Status, entity.CreatedAt }); diff --git a/Tiku.Infrastructure/Persistence/Configurations/QuestionConfigurations.cs b/Tiku.Infrastructure/Persistence/Configurations/QuestionConfigurations.cs index ca0cf01..e0eb187 100644 --- a/Tiku.Infrastructure/Persistence/Configurations/QuestionConfigurations.cs +++ b/Tiku.Infrastructure/Persistence/Configurations/QuestionConfigurations.cs @@ -14,7 +14,6 @@ internal sealed class QuestionBankConfiguration : IEntityTypeConfiguration entity.Name).HasMaxLength(300); - builder.Property(entity => entity.SourceScope).HasSnakeCaseEnum(); builder.Property(entity => entity.Status).HasSnakeCaseEnum(); builder.Property(entity => entity.Metadata).IsJson("{}"); @@ -94,7 +93,7 @@ internal sealed class QuestionVersionConfiguration : IEntityTypeConfiguration entity.SourceHash).HasMaxLength(128); builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()"); - builder.HasIndex(entity => new { entity.QuestionId, entity.VersionNo }).IsUnique(); + builder.HasIndex(entity => new { entity.TenantId, entity.QuestionId, entity.VersionNo }).IsUnique(); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, entity.QuestionId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) diff --git a/Tiku.Infrastructure/Persistence/Configurations/TaxonomyConfigurations.cs b/Tiku.Infrastructure/Persistence/Configurations/TaxonomyConfigurations.cs new file mode 100644 index 0000000..be5f49d --- /dev/null +++ b/Tiku.Infrastructure/Persistence/Configurations/TaxonomyConfigurations.cs @@ -0,0 +1,53 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Tiku.Domain.Catalog; +using Tiku.Domain.QuestionBanks; + +namespace Tiku.Infrastructure.Persistence.Configurations; + +internal sealed class TaxonomyNodeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ConfigureTenantEntity("taxonomy_nodes"); + builder.ConfigureTimestamps(); + builder.Property(entity => entity.NodeType).HasSnakeCaseEnum(); + builder.Property(entity => entity.Code).HasMaxLength(100); + builder.Property(entity => entity.Name).HasMaxLength(300); + builder.Property(entity => entity.Path).HasColumnType("ltree"); + builder.Property(entity => entity.Metadata).IsJson("{}"); + builder.HasIndex(entity => new { entity.TenantId, entity.Code }).IsUnique(); + builder.HasOne().WithMany() + .HasForeignKey(entity => new { TenantId = entity.ParentOwnerTenantId, Id = entity.ParentId }) + .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) + .OnDelete(DeleteBehavior.Restrict); + builder.ToTable(table => table.HasCheckConstraint( + "ck_taxonomy_nodes_parent_pair", + "(parent_owner_tenant_id is null) = (parent_id is null)")); + } +} + +internal sealed class QuestionTaxonomyAssignmentConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ConfigureEntity("question_taxonomy_assignments"); + builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id }); + builder.HasIndex(entity => new + { + entity.TenantId, + entity.QuestionId, + entity.TaxonomyOwnerTenantId, + entity.TaxonomyNodeId + }).IsUnique(); + builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()"); + builder.HasOne().WithMany() + .HasForeignKey(entity => new { entity.TenantId, entity.QuestionId }) + .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) + .OnDelete(DeleteBehavior.Cascade); + builder.HasOne().WithMany() + .HasForeignKey(entity => new { TenantId = entity.TaxonomyOwnerTenantId, Id = entity.TaxonomyNodeId }) + .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) + .OnDelete(DeleteBehavior.Restrict); + } +} diff --git a/Tiku.Infrastructure/Persistence/Configurations/TenancyConfigurations.cs b/Tiku.Infrastructure/Persistence/Configurations/TenancyConfigurations.cs index 3274711..c22763b 100644 --- a/Tiku.Infrastructure/Persistence/Configurations/TenancyConfigurations.cs +++ b/Tiku.Infrastructure/Persistence/Configurations/TenancyConfigurations.cs @@ -23,6 +23,9 @@ internal sealed class TenantConfiguration : IEntityTypeConfiguration builder.HasIndex(entity => entity.Slug).IsUnique(); builder.HasIndex(entity => entity.LegacyId).IsUnique(); + builder.HasIndex(entity => entity.Mode) + .IsUnique() + .HasFilter("mode = 'platform_owned'"); builder.HasOne() .WithMany() @@ -67,7 +70,10 @@ internal sealed class TenantDomainConfiguration : IEntityTypeConfiguration entity.DomainType).HasSnakeCaseEnum(); builder.Property(entity => entity.Status).HasSnakeCaseEnum(); builder.Property(entity => entity.VerificationToken).HasMaxLength(256); - builder.HasIndex(entity => entity.Host).IsUnique(); + builder.Property(entity => entity.LastFailureReason).HasMaxLength(2000); + builder.HasIndex(entity => entity.Host) + .IsUnique() + .HasAnnotation("Tiku:GlobalUnique", true); builder.HasIndex(entity => new { entity.TenantId, entity.IsPrimary }) .IsUnique() .HasFilter("is_primary"); @@ -114,3 +120,29 @@ internal sealed class TenantSettingsConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ConfigureTenantEntity("tenant_frontend_configs"); + builder.ConfigureTimestamps(); + builder.HasIndex(entity => entity.TenantId).IsUnique(); + builder.Property(entity => entity.ConfigVersion).IsConcurrencyToken(); + builder.Property(entity => entity.PublishedBranding).IsJson("{}"); + builder.Property(entity => entity.PublishedTheme).IsJson("{}"); + builder.Property(entity => entity.PublishedFeatures).IsJson("{}"); + builder.Property(entity => entity.PublishedNavigation).IsJson("[]"); + builder.Property(entity => entity.PublishedHomeModules).IsJson("[]"); + builder.Property(entity => entity.DraftBranding).IsJson("{}"); + builder.Property(entity => entity.DraftTheme).IsJson("{}"); + builder.Property(entity => entity.DraftFeatures).IsJson("{}"); + builder.Property(entity => entity.DraftNavigation).IsJson("[]"); + builder.Property(entity => entity.DraftHomeModules).IsJson("[]"); + builder.ToTable(table => + { + table.HasCheckConstraint("ck_tenant_frontend_configs_schema_version", "schema_version > 0"); + table.HasCheckConstraint("ck_tenant_frontend_configs_config_version", "config_version > 0"); + }); + } +} diff --git a/Tiku.Infrastructure/Persistence/Configurations/TenantOperationsConfigurations.cs b/Tiku.Infrastructure/Persistence/Configurations/TenantOperationsConfigurations.cs index 92b2582..6f7e409 100644 --- a/Tiku.Infrastructure/Persistence/Configurations/TenantOperationsConfigurations.cs +++ b/Tiku.Infrastructure/Persistence/Configurations/TenantOperationsConfigurations.cs @@ -111,7 +111,9 @@ internal sealed class AuthSessionConfiguration : IEntityTypeConfiguration entity.IpAddress).HasMaxLength(64); builder.Property(entity => entity.UserAgent).HasMaxLength(1000); builder.Property(entity => entity.Metadata).IsJson("{}"); - builder.HasIndex(entity => entity.TokenHash).IsUnique(); + builder.HasIndex(entity => entity.TokenHash) + .IsUnique() + .HasAnnotation("Tiku:GlobalUnique", true); builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.ExpiresAt }) .HasFilter("revoked_at is null"); diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260725220742_InitialSchema.Designer.cs b/Tiku.Infrastructure/Persistence/Migrations/20260725220742_InitialSchema.Designer.cs deleted file mode 100644 index 86cd65d..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260725220742_InitialSchema.Designer.cs +++ /dev/null @@ -1,15302 +0,0 @@ -// -using System; -using System.Text.Json; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using Tiku.Infrastructure.Persistence; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - [DbContext(typeof(TikuDbContext))] - [Migration("20260725220742_InitialSchema")] - partial class InitialSchema - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext"); - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "ltree"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("category_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("SvipQuestionLimit") - .HasColumnType("integer") - .HasColumnName("svip_question_limit"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_categories"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_categories_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_categories_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_categories_tenant_id_node_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_categories_tenant_id_subject_id"); - - b.ToTable("categories", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("StudyTips") - .HasColumnType("text") - .HasColumnName("study_tips"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_majors"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_majors_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_majors_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_majors_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_majors_tenant_id_school_id"); - - b.ToTable("majors", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyModuleId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_module_id"); - - b.Property("LegacyParentId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_parent_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_module_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_module_nodes_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_module_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_module_nodes_tenant_id_module_id"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_module_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_module_nodes_tenant_id_region_id"); - - b.ToTable("module_nodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FullName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("full_name"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsHot") - .HasColumnType("boolean") - .HasColumnName("is_hot"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Pinyin") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("pinyin"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_regions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_regions_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_regions_tenant_id_legacy_id"); - - b.ToTable("regions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsPrimarySchoolModule") - .HasColumnType("boolean") - .HasColumnName("is_primary_school_module"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TextColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("text_color"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_region_modules"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_region_modules_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_region_modules_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_region_modules_tenant_id_region_id"); - - b.ToTable("region_modules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ProfessionalExamDate") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("professional_exam_date"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_schools"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_schools_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_schools_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_schools_tenant_id_module_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_schools_tenant_id_region_id"); - - b.ToTable("schools", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_subjects_tenant_id_module_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_subjects_tenant_id_node_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_subjects_tenant_id_school_id"); - - b.ToTable("subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AgentUserId") - .HasColumnType("uuid") - .HasColumnName("agent_user_id"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponRedemptionId") - .HasColumnType("uuid") - .HasColumnName("coupon_redemption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("IsUsed") - .HasColumnType("boolean") - .HasColumnName("is_used"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("SoldTo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("sold_to"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitPriceCents") - .HasColumnType("integer") - .HasColumnName("unit_price_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UsedBy") - .HasColumnType("uuid") - .HasColumnName("used_by"); - - b.Property("UsedRegionId") - .HasColumnType("uuid") - .HasColumnName("used_region_id"); - - b.HasKey("Id") - .HasName("pk_activation_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_activation_codes_tenant_id_id"); - - b.HasIndex("AgentUserId") - .HasDatabaseName("ix_activation_codes_agent_user_id"); - - b.HasIndex("UsedBy") - .HasDatabaseName("ix_activation_codes_used_by"); - - b.HasIndex("TenantId", "BatchId") - .HasDatabaseName("ix_activation_codes_tenant_id_batch_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_code"); - - b.HasIndex("TenantId", "CouponRedemptionId") - .HasDatabaseName("ix_activation_codes_tenant_id_coupon_redemption_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UsedRegionId") - .HasDatabaseName("ix_activation_codes_tenant_id_used_region_id"); - - b.ToTable("activation_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CampaignName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("campaign_name"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CostPriceCents") - .HasColumnType("integer") - .HasColumnName("cost_price_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("DefaultUnitPriceCents") - .HasColumnType("integer") - .HasColumnName("default_unit_price_cents"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_code_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_code_batches_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_code_batches_tenant_id_legacy_id"); - - b.ToTable("code_batches", null, t => - { - t.HasCheckConstraint("ck_code_batches_amounts", "default_unit_price_cents >= 0 and cost_price_cents >= 0"); - - t.HasCheckConstraint("ck_code_batches_counts", "total_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillDate") - .HasColumnType("date") - .HasColumnName("bill_date"); - - b.Property("BillType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bill_type"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DuplicateCount") - .HasColumnType("integer") - .HasColumnName("duplicate_count"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IgnoredCount") - .HasColumnType("integer") - .HasColumnName("ignored_count"); - - b.Property("MatchedCount") - .HasColumnType("integer") - .HasColumnName("matched_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MismatchCount") - .HasColumnType("integer") - .HasColumnName("mismatch_count"); - - b.Property("MissingLocalCount") - .HasColumnType("integer") - .HasColumnName("missing_local_count"); - - b.Property("MissingProviderCount") - .HasColumnType("integer") - .HasColumnName("missing_provider_count"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source"); - - b.Property("SourceHash") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("source_name"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_batches_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_batches_created_by"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "Provider", "BillDate", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_provider_bill_dat~"); - - b.ToTable("commerce_reconciliation_batches", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_batches_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_batches_counts", "total_count >= 0 and matched_count >= 0 and mismatch_count >= 0 and missing_local_count >= 0 and missing_provider_count >= 0 and duplicate_count >= 0 and ignored_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("AssignedTo") - .HasColumnType("uuid") - .HasColumnName("assigned_to"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("IssueNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_no"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolutionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("resolution_type"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("summary"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issues_tenant_id_id"); - - b.HasIndex("AssignedTo") - .HasDatabaseName("ix_commerce_reconciliation_issues_assigned_to"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_created_by"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_resolved_by"); - - b.HasIndex("TenantId", "IssueNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_issue_no"); - - b.HasIndex("TenantId", "ItemId") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_item_id") - .HasFilter("item_id is not null and status in ('open', 'investigating', 'escalated')"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "PaymentId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_payment_id_created~"); - - b.HasIndex("TenantId", "AssignedTo", "Status", "DueAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_assigned_to_status~"); - - b.HasIndex("TenantId", "BatchId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_batch_id_status_cr~"); - - b.HasIndex("TenantId", "Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_status_severity_cr~"); - - b.ToTable("commerce_reconciliation_issues", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_issues_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("IssueId") - .HasColumnType("uuid") - .HasColumnName("issue_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issue_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issue_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_actor_user_id"); - - b.HasIndex("TenantId", "IssueId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_tenant_id_issue_id_cre~"); - - b.ToTable("commerce_reconciliation_issue_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LocalStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("local_status"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider_status"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("RefundedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("refunded_at"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_items_tenant_id_id"); - - b.HasIndex("BatchId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_items_batch_id_row_no"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "BatchId", "MatchStatus", "RowNo") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_batch_id_match_stat~"); - - b.HasIndex("TenantId", "Provider", "ProviderTradeNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_provider_provider_t~"); - - b.HasIndex("TenantId", "RefundNo", "ProviderRefundNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_no_provider_~"); - - b.ToTable("commerce_reconciliation_items", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_items_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_items_row", "row_no > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_refund_events_actor_user_id"); - - b.HasIndex("TenantId", "RefundRequestId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_events_tenant_id_refund_request_id_created_~"); - - b.ToTable("commerce_refund_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CancelledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("cancelled_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementAction") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entitlement_action"); - - b.Property("FailedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("failed_at"); - - b.Property("FailureReason") - .HasColumnType("text") - .HasColumnName("failure_reason"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("ProcessedBy") - .HasColumnType("uuid") - .HasColumnName("processed_by"); - - b.Property("Provider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("Reason") - .HasColumnType("text") - .HasColumnName("reason"); - - b.Property("RefundNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RequestedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("requested_at") - .HasDefaultValueSql("now()"); - - b.Property("RequestedBy") - .HasColumnType("uuid") - .HasColumnName("requested_by"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SucceededAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("succeeded_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_requests"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_requests_tenant_id_id"); - - b.HasIndex("ProcessedBy") - .HasDatabaseName("ix_commerce_refund_requests_processed_by"); - - b.HasIndex("RequestedBy") - .HasDatabaseName("ix_commerce_refund_requests_requested_by"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commerce_refund_requests_reviewed_by"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundNo") - .IsUnique() - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_refund_no"); - - b.HasIndex("TenantId", "OrderId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_order_id_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_status_created_at"); - - b.ToTable("commerce_refund_requests", null, t => - { - t.HasCheckConstraint("ck_commerce_refund_requests_amount", "amount_cents > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("discount_type"); - - b.Property("DiscountValue") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("discount_value"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MaxUses") - .HasColumnType("integer") - .HasColumnName("max_uses"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("ValidFrom") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_from"); - - b.Property("ValidTo") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_to"); - - b.HasKey("Id") - .HasName("pk_coupons"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupons_tenant_id_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_code"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupons_tenant_id_plan_id"); - - b.ToTable("coupons", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClaimedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("claimed_at"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponId") - .HasColumnType("uuid") - .HasColumnName("coupon_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountAppliedCents") - .HasColumnType("integer") - .HasColumnName("discount_applied_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_coupon_redemptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupon_redemptions_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_coupon_redemptions_user_id"); - - b.HasIndex("TenantId", "CouponId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_coupon_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_region_id"); - - b.HasIndex("TenantId", "OrderId", "UserId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_order_id_user_id") - .HasFilter("order_id is not null"); - - b.HasIndex("TenantId", "UserId", "CouponId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_user_id_coupon_id") - .HasFilter("coupon_id is not null"); - - b.ToTable("coupon_redemptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("entitlement_type"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("LegacySourceId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_source_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("RevokedBy") - .HasColumnType("uuid") - .HasColumnName("revoked_by"); - - b.Property("RevokedReason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("revoked_reason"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("StartsAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at") - .HasDefaultValueSql("now()"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_entitlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_entitlements_tenant_id_id"); - - b.HasIndex("RevokedBy") - .HasDatabaseName("ix_entitlements_revoked_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_entitlements_user_id"); - - b.HasIndex("TenantId", "UserId", "Status", "ExpiresAt") - .HasDatabaseName("ix_entitlements_tenant_id_user_id_status_expires_at"); - - b.ToTable("entitlements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPlanId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_plan_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("OrderNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PayMethod") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_method"); - - b.Property("PayProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_provider"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("ProductName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("product_name"); - - b.Property("ProductType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("product_type"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("trade_no"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_orders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_orders_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_orders_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderNo") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_order_no"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_orders_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_orders_tenant_id_region_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_orders_tenant_id_user_id_created_at"); - - b.ToTable("orders", null, t => - { - t.HasCheckConstraint("ck_orders_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_orders_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("Quantity") - .HasColumnType("integer") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalAmountCents") - .HasColumnType("integer") - .HasColumnName("total_amount_cents"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_order_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_order_items_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_order_items_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_order_items_tenant_id_order_id"); - - b.ToTable("order_items", null, t => - { - t.HasCheckConstraint("ck_order_items_amounts", "unit_amount_cents >= 0 and total_amount_cents >= 0"); - - t.HasCheckConstraint("ck_order_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyOrderId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_order_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payments_tenant_id_id"); - - b.HasIndex("Provider", "ProviderTradeNo") - .IsUnique() - .HasDatabaseName("ix_payments_provider_provider_trade_no") - .HasFilter("provider_trade_no is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_payments_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId", "UpdatedAt") - .HasDatabaseName("ix_payments_tenant_id_order_id_updated_at"); - - b.ToTable("payments", null, t => - { - t.HasCheckConstraint("ck_payments_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_payments_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("EventId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("event_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("SignatureValid") - .HasColumnType("boolean") - .HasColumnName("signature_valid"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_payment_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payment_events_tenant_id_id"); - - b.HasIndex("Provider", "EventId") - .IsUnique() - .HasDatabaseName("ix_payment_events_provider_event_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_payment_events_tenant_id_payment_id"); - - b.ToTable("payment_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Cover") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cover"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DetailImages") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("detail_images") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPriceText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_price_text"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("PreviewIframe") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_iframe"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_products"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_products_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_products_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "Type", "SortOrder") - .HasDatabaseName("ix_products_tenant_id_region_id_type_sort_order"); - - b.ToTable("products", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Badge") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("badge"); - - b.Property("CouponOnly") - .HasColumnType("boolean") - .HasColumnName("coupon_only"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OriginalPriceCents") - .HasColumnType("integer") - .HasColumnName("original_price_cents"); - - b.Property("PerDayLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("per_day_label"); - - b.Property("PriceCents") - .HasColumnType("integer") - .HasColumnName("price_cents"); - - b.Property("Recommended") - .HasColumnType("boolean") - .HasColumnName("recommended"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VpEnabled") - .HasColumnType("boolean") - .HasColumnName("vp_enabled"); - - b.Property("VpProductId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("vp_product_id"); - - b.HasKey("Id") - .HasName("pk_svip_plans"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_svip_plans_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_svip_plans_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_svip_plans_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("svip_plans", null, t => - { - t.HasCheckConstraint("ck_svip_plans_days", "days >= 0"); - - t.HasCheckConstraint("ck_svip_plans_price", "price_cents >= 0 and (original_price_cents is null or original_price_cents >= 0)"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_payment_accounts"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_payment_accounts_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_payment_accounts_tenant_id_provider"); - - b.ToTable("tenant_payment_accounts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillingCycle") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("billing_cycle"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PlanCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("plan_code"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_subscriptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_subscriptions_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "ExpiresAt") - .HasDatabaseName("ix_tenant_subscriptions_tenant_id_status_expires_at"); - - b.ToTable("tenant_subscriptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MetricKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("metric_key"); - - b.Property("MetricValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("metric_value"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_tenant_usage_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_usage_records_tenant_id_id"); - - b.HasIndex("TenantId", "MetricKey", "PeriodStart", "PeriodEnd") - .HasDatabaseName("ix_tenant_usage_records_tenant_id_metric_key_period_start_peri~"); - - b.ToTable("tenant_usage_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContextPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("context_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("GeneratedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("generated_at"); - - b.Property("InputPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("input_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Model") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("model"); - - b.Property("PromptVersion") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("prompt_version"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("ResultPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("result_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_ai_recommendation_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_ai_recommendation_reports_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_ai_recommendation_reports_user_id"); - - b.HasIndex("TenantId", "RegionId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_region_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_user_id_created_at"); - - b.ToTable("ai_recommendation_reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetKey") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_app_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_app_assets_tenant_id_id"); - - b.HasIndex("TenantId", "AssetKey") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_asset_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_legacy_id"); - - b.ToTable("app_assets", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssetKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("AssetType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("asset_type"); - - b.Property("Bucket") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bucket"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("ChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("checksum_sha256"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DownloadCount") - .HasColumnType("integer") - .HasColumnName("download_count"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("FileSizeBytes") - .HasColumnType("bigint") - .HasColumnName("file_size_bytes"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MimeType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mime_type"); - - b.Property("ObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("object_key"); - - b.Property("PreviewObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("preview_object_key"); - - b.Property("PreviewStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("preview_status"); - - b.Property("PreviewUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_url"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SecurityFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScanProvider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("security_scan_provider"); - - b.Property("SecurityScanStatus") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("not_required") - .HasColumnName("security_scan_status"); - - b.Property("SecurityScanSummary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_scan_summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScannedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("security_scanned_at"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StorageProvider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("storage_provider"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UploadStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("upload_status"); - - b.Property("VerificationDetails") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("verification_details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.Property("VerifiedBy") - .HasColumnType("uuid") - .HasColumnName("verified_by"); - - b.Property("VerifiedChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("verified_checksum_sha256"); - - b.Property("VerifiedSizeBytes") - .HasColumnType("bigint") - .HasColumnName("verified_size_bytes"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_assets_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_assets_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_assets_updated_by"); - - b.HasIndex("VerifiedBy") - .HasDatabaseName("ix_content_assets_verified_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_content_assets_tenant_id_category_id"); - - b.HasIndex("TenantId", "ChecksumSha256") - .HasDatabaseName("ix_content_assets_tenant_id_checksum_sha256") - .HasFilter("checksum_sha256 is not null"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_content_assets_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_assets_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PreviewStatus") - .HasDatabaseName("ix_content_assets_tenant_id_preview_status") - .HasFilter("preview_status <> 'none'"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_assets_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_content_assets_tenant_id_subject_id"); - - b.HasIndex("TenantId", "StorageProvider", "Bucket", "ObjectKey") - .HasDatabaseName("ix_content_assets_tenant_id_storage_provider_bucket_object_key"); - - b.HasIndex("TenantId", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_upload_status_status_updated_at"); - - b.HasIndex("TenantId", "SecurityScanStatus", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_security_scan_status_upload_status~"); - - b.HasIndex("TenantId", "Status", "Visibility", "AssetType", "RegionId", "SubjectId", "CategoryId", "SortOrder") - .HasDatabaseName("ix_content_assets_tenant_id_status_visibility_asset_type_regio~"); - - b.ToTable("content_assets", null, t => - { - t.HasCheckConstraint("ck_content_assets_download_count", "download_count >= 0"); - - t.HasCheckConstraint("ck_content_assets_file_size", "file_size_bytes is null or file_size_bytes >= 0"); - - t.HasCheckConstraint("ck_content_assets_verified_checksum", "verified_checksum_sha256 is null or verified_checksum_sha256 ~ '^[a-f0-9]{64}$'"); - - t.HasCheckConstraint("ck_content_assets_verified_size", "verified_size_bytes is null or verified_size_bytes >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_type"); - - b.Property("ActorRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("actor_role"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("AssetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("asset_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DenyCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("deny_code"); - - b.Property("Disposition") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("disposition"); - - b.Property("ExpiresInSeconds") - .HasColumnType("integer") - .HasColumnName("expires_in_seconds"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("SignatureMode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("signature_mode"); - - b.Property("StorageProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("storage_provider"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("Visibility") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_asset_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_content_asset_access_events_user_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_asset_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_user_id_created_at") - .HasFilter("user_id is not null"); - - b.HasIndex("TenantId", "AccessType", "Result", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_access_type_result_cr~"); - - b.ToTable("content_asset_access_events", null, t => - { - t.HasCheckConstraint("ck_content_asset_access_events_expiry", "expires_in_seconds is null or expires_in_seconds > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.PrimitiveCollection("IssueCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("issue_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RiskLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("risk_level"); - - b.Property("ScanStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scan_status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_content_asset_security_scan_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_security_scan_events_tenant_id_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_asset_id_creat~"); - - b.HasIndex("TenantId", "ScanStatus", "RiskLevel", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_scan_status_ri~"); - - b.ToTable("content_asset_security_scan_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("description"); - - b.Property("EntryKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("entry_key"); - - b.Property("EntryType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entry_type"); - - b.Property("Icon") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LayoutConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("layout_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_entries_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_entries_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_entries_updated_by"); - - b.HasIndex("TenantId", "EntryKey") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_entry_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "EntryType", "IsActive", "SortOrder") - .HasDatabaseName("ix_content_entries_tenant_id_region_id_entry_type_is_active_so~"); - - b.ToTable("content_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("Message") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("message"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "ItemId") - .HasDatabaseName("ix_content_import_issues_tenant_id_item_id"); - - b.HasIndex("TenantId", "JobId", "Severity", "RowNo") - .HasDatabaseName("ix_content_import_issues_tenant_id_job_id_severity_row_no"); - - b.ToTable("content_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("content_hash"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExternalId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("external_id"); - - b.Property("IssuesCount") - .HasColumnType("integer") - .HasColumnName("issues_count"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("SourcePayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_items_tenant_id_id"); - - b.HasIndex("JobId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_content_import_items_job_id_row_no"); - - b.HasIndex("TenantId", "JobId", "Status", "RowNo") - .HasDatabaseName("ix_content_import_items_tenant_id_job_id_status_row_no"); - - b.ToTable("content_import_items", null, t => - { - t.HasCheckConstraint("ck_content_import_items_issues_count", "issues_count >= 0"); - - t.HasCheckConstraint("ck_content_import_items_row_no", "row_no >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DryRun") - .HasColumnType("boolean") - .HasColumnName("dry_run"); - - b.Property("ErrorCount") - .HasColumnType("integer") - .HasColumnName("error_count"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("ImportType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("import_type"); - - b.Property("InsertedCount") - .HasColumnType("integer") - .HasColumnName("inserted_count"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SkippedCount") - .HasColumnType("integer") - .HasColumnName("skipped_count"); - - b.Property("SourceFormat") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_format"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TargetCategoryId") - .HasColumnType("uuid") - .HasColumnName("target_category_id"); - - b.Property("TargetContentNodeId") - .HasColumnType("uuid") - .HasColumnName("target_content_node_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TargetRegionId") - .HasColumnType("uuid") - .HasColumnName("target_region_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedCount") - .HasColumnType("integer") - .HasColumnName("updated_count"); - - b.Property("ValidCount") - .HasColumnType("integer") - .HasColumnName("valid_count"); - - b.Property("WarningCount") - .HasColumnType("integer") - .HasColumnName("warning_count"); - - b.HasKey("Id") - .HasName("pk_content_import_jobs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_jobs_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_import_jobs_created_by"); - - b.HasIndex("TenantId", "TargetCategoryId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_category_id"); - - b.HasIndex("TenantId", "TargetContentNodeId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_content_node_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_question_bank_id"); - - b.HasIndex("TenantId", "TargetRegionId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_region_id"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_subject_id"); - - b.HasIndex("TenantId", "ImportType", "Status", "CreatedAt") - .HasDatabaseName("ix_content_import_jobs_tenant_id_import_type_status_created_at"); - - b.ToTable("content_import_jobs", null, t => - { - t.HasCheckConstraint("ck_content_import_jobs_counts", "total_count >= 0 and valid_count >= 0 and error_count >= 0 and warning_count >= 0 and inserted_count >= 0 and updated_count >= 0 and skipped_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Depth") - .HasColumnType("integer") - .HasColumnName("depth"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsLeaf") - .HasColumnType("boolean") - .HasColumnName("is_leaf"); - - b.Property("IsSelectable") - .HasColumnType("boolean") - .HasColumnName("is_selectable"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MarkerConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("marker_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MarkerType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("marker_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("node_key"); - - b.Property("NodeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("node_type"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasColumnType("ltree") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_content_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_nodes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_nodes_created_by"); - - b.HasIndex("Path") - .HasDatabaseName("ix_content_nodes_path"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Path"), "gist"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_nodes_updated_by"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MarkerType") - .HasDatabaseName("ix_content_nodes_tenant_id_marker_type") - .HasFilter("marker_type is not null"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_content_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_nodes_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "NodeKey") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_node_key"); - - b.HasIndex("TenantId", "EntryId", "ParentId", "SortOrder") - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_parent_id_sort_order"); - - b.ToTable("content_nodes", null, t => - { - t.HasCheckConstraint("ck_content_nodes_depth", "depth >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_chapters"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_chapters_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_chapters_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_subject_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_chapters_tenant_id_entry_id_content_node_id_subjec~"); - - b.ToTable("handbook_chapters", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChapterId") - .HasColumnType("uuid") - .HasColumnName("chapter_id"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_entries_tenant_id_id"); - - b.HasIndex("TenantId", "ChapterId") - .HasDatabaseName("ix_handbook_entries_tenant_id_chapter_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_entries_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "ChapterId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_entries_tenant_id_entry_id_content_node_id_chapter~"); - - b.ToTable("handbook_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_school_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_subjects_tenant_id_entry_id_content_node_id_region~"); - - b.ToTable("handbook_subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_images"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_images_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_images_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsPublic") - .HasDatabaseName("ix_images_tenant_id_category_is_public"); - - b.ToTable("images", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssemblyType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assembly_type"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PassScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("pass_score"); - - b.Property("QuestionLimit") - .HasColumnType("integer") - .HasColumnName("question_limit"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Rules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Sections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sections") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_practice_blueprints"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_blueprints_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_practice_blueprints_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_practice_blueprints_updated_by"); - - b.HasIndex("TenantId", "CollectionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_collection_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_practice_blueprints_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_region_id"); - - b.HasIndex("TenantId", "NodeId", "CollectionId", "Mode", "Status", "SortOrder") - .HasDatabaseName("ix_practice_blueprints_tenant_id_node_id_collection_id_mode_st~"); - - b.ToTable("practice_blueprints", null, t => - { - t.HasCheckConstraint("ck_practice_blueprints_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_practice_blueprints_question_limit", "question_limit is null or question_limit > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("AllowedPlanCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("allowed_plan_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.PrimitiveCollection("AllowedRegionIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_region_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedSubjectIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_subject_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedTenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("GrantScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("grant_scope"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_bank_grants"); - - b.HasIndex("AllowedPlanCodes") - .HasDatabaseName("ix_question_bank_grants_allowed_plan_codes"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedPlanCodes"), "gin"); - - b.HasIndex("AllowedTenantIds") - .HasDatabaseName("ix_question_bank_grants_allowed_tenant_ids"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedTenantIds"), "gin"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_bank_grants_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_bank_grants_updated_by"); - - b.HasIndex("SourceQuestionBankId", "Status", "StartsAt", "ExpiresAt") - .HasDatabaseName("ix_question_bank_grants_source_question_bank_id_status_starts_~"); - - b.ToTable("question_bank_grants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CollectionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("collection_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Filters") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("filters") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_collections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collections_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_collections_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_collections_updated_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_question_collections_tenant_id_category_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_question_collections_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_collections_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_question_collections_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_collections_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_question_collections_tenant_id_subject_id"); - - b.HasIndex("TenantId", "NodeId", "Status", "SortOrder") - .HasDatabaseName("ix_question_collections_tenant_id_node_id_status_sort_order"); - - b.ToTable("question_collections", null, t => - { - t.HasCheckConstraint("ck_question_collections_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_question_collections_question_count", "question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Required") - .HasColumnType("boolean") - .HasColumnName("required"); - - b.Property("Score") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_collection_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collection_items_tenant_id_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_question_collection_items_tenant_id_question_id"); - - b.HasIndex("TenantId", "CollectionId", "QuestionId") - .IsUnique() - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_question_~"); - - b.HasIndex("TenantId", "CollectionId", "SectionKey", "SortOrder") - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_section_k~"); - - b.ToTable("question_collection_items", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Types") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("types") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_type_groups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_type_groups_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_type_groups_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_question_type_groups_tenant_id_subject_id_is_active_sort_or~"); - - b.ToTable("question_type_groups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("LegacyVideoId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_video_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoId") - .HasColumnType("uuid") - .HasColumnName("video_id"); - - b.Property("VideoType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("video_type"); - - b.HasKey("Id") - .HasName("pk_question_videos"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_videos_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "VideoId") - .HasDatabaseName("ix_question_videos_tenant_id_video_id"); - - b.HasIndex("TenantId", "QuestionId", "VideoId", "VideoType") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_question_id_video_id_video_type"); - - b.ToTable("question_videos", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("SourceSubjectId") - .HasColumnType("uuid") - .HasColumnName("source_subject_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacySourceSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_source_subject_id"); - - b.Property("LegacyTargetSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_target_subject_id"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.HasKey("TenantId", "SourceSubjectId", "TargetSubjectId") - .HasName("pk_subject_shares"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_subject_shares_tenant_id_target_subject_id"); - - b.ToTable("subject_shares", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AdoptionMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("adoption_mode"); - - b.Property("CopiedQuestionCount") - .HasColumnType("integer") - .HasColumnName("copied_question_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("GrantId") - .HasColumnType("uuid") - .HasColumnName("grant_id"); - - b.Property("LastSyncedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_synced_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("SourceSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SyncStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("sync_status"); - - b.Property("TargetCollectionId") - .HasColumnType("uuid") - .HasColumnName("target_collection_id"); - - b.Property("TargetEntryId") - .HasColumnType("uuid") - .HasColumnName("target_entry_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_question_bank_adoptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_question_bank_adoptions_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_created_by"); - - b.HasIndex("GrantId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_grant_id"); - - b.HasIndex("SourceQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_source_question_bank_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_updated_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .IsUnique() - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_source_question_ba~"); - - b.HasIndex("TenantId", "TargetCollectionId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_collection_~"); - - b.HasIndex("TenantId", "TargetEntryId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_entry_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_question_ba~"); - - b.HasIndex("TenantId", "Status", "UpdatedAt") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_status_updated_at"); - - b.ToTable("tenant_question_bank_adoptions", null, t => - { - t.HasCheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FavoritedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("favorited_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Note") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.HasKey("Id") - .HasName("pk_user_word_favorites"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_favorites_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_favorites_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_favorites_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_favorites_tenant_id_user_id_word_id"); - - b.ToTable("user_word_favorites", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CorrectStreak") - .HasColumnType("integer") - .HasColumnName("correct_streak"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DueLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("due_level"); - - b.Property("EaseFactor") - .ValueGeneratedOnAdd() - .HasPrecision(4, 2) - .HasColumnType("numeric(4,2)") - .HasDefaultValue(2.50m) - .HasColumnName("ease_factor"); - - b.Property("LastResult") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("last_result"); - - b.Property("LastReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_review_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_review_at"); - - b.Property("ReviewCount") - .HasColumnType("integer") - .HasColumnName("review_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_user_word_progress"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_progress_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_progress_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_progress_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_word_id"); - - b.HasIndex("TenantId", "UserId", "NextReviewAt", "Status", "DueLevel") - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_next_review_at_status_~"); - - b.ToTable("user_word_progress", null, t => - { - t.HasCheckConstraint("ck_user_word_progress_correct_streak", "correct_streak >= 0"); - - t.HasCheckConstraint("ck_user_word_progress_ease_factor", "ease_factor >= 1.30 and ease_factor <= 3.00"); - - t.HasCheckConstraint("ck_user_word_progress_review_count", "review_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsGeneral") - .HasColumnType("boolean") - .HasColumnName("is_general"); - - b.Property("KnowledgeTags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("knowledge_tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ThumbnailUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("thumbnail_url"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("video_url"); - - b.HasKey("Id") - .HasName("pk_video_explanations"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_video_explanations_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_video_explanations_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SourceHash") - .HasDatabaseName("ix_video_explanations_tenant_id_source_hash") - .HasFilter("source_hash is not null"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_video_explanations_tenant_id_subject_id_is_active_sort_order"); - - b.ToTable("video_explanations", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WordCount") - .HasColumnType("integer") - .HasColumnName("word_count"); - - b.HasKey("Id") - .HasName("pk_vocabulary_units"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_units_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_units_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_units_tenant_id_entry_id_content_node_id_region_~"); - - b.ToTable("vocabulary_units", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Example") - .HasColumnType("text") - .HasColumnName("example"); - - b.Property("ExampleTranslation") - .HasColumnType("text") - .HasColumnName("example_translation"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Meaning") - .HasColumnType("text") - .HasColumnName("meaning"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phonetic") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("phonetic"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitId") - .HasColumnType("uuid") - .HasColumnName("unit_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Word") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("word"); - - b.HasKey("Id") - .HasName("pk_vocabulary_words"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_words_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_words_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_words_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UnitId", "IsActive", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_vocabulary_words_tenant_id_unit_id_is_active_sort_order_cre~"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "UnitId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_words_tenant_id_entry_id_content_node_id_unit_id~"); - - b.ToTable("vocabulary_words", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("EffectiveRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("effective_rate"); - - b.Property("GeneratedBy") - .HasColumnType("uuid") - .HasColumnName("generated_by"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidBy") - .HasColumnType("uuid") - .HasColumnName("paid_by"); - - b.Property("PaidUserCount") - .HasColumnType("integer") - .HasColumnName("paid_user_count"); - - b.Property("PaymentAccount") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("payment_account"); - - b.Property("PaymentMethod") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_method"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("SettlementNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("settlement_no"); - - b.Property("SourceCount") - .HasColumnType("integer") - .HasColumnName("source_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlements_tenant_id_id"); - - b.HasIndex("GeneratedBy") - .HasDatabaseName("ix_commission_settlements_generated_by"); - - b.HasIndex("PaidBy") - .HasDatabaseName("ix_commission_settlements_paid_by"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlements_referrer_user_id"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commission_settlements_reviewed_by"); - - b.HasIndex("TenantId", "SettlementNo") - .IsUnique() - .HasDatabaseName("ix_commission_settlements_tenant_id_settlement_no"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart") - .HasDatabaseName("ix_commission_settlements_referrer_start"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart", "PeriodEnd") - .IsUnique() - .HasDatabaseName("ux_commission_settlements_referrer_period"); - - b.ToTable("commission_settlements", null, t => - { - t.HasCheckConstraint("ck_commission_settlements_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_counts", "source_count >= 0 and paid_user_count >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_period", "period_end >= period_start"); - - t.HasCheckConstraint("ck_commission_settlements_rates", "default_rate >= 0 and default_rate <= 1 and (effective_rate is null or (effective_rate >= 0 and effective_rate <= 1))"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AttributionType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("attribution_type"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RateSource") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("rate_source"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("SettlementId") - .HasColumnType("uuid") - .HasColumnName("settlement_id"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_no"); - - b.Property("SourcePaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("source_paid_at"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlement_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlement_items_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlement_items_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_commission_settlement_items_student_user_id"); - - b.HasIndex("TenantId", "SettlementId") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_settlement_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "SourcePaidAt") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_referrer_user_id_sour~"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .IsUnique() - .HasDatabaseName("ix_commission_settlement_items_tenant_id_source_type_source_id"); - - b.ToTable("commission_settlement_items", null, t => - { - t.HasCheckConstraint("ck_commission_settlement_items_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlement_items_rate", "commission_rate >= 0 and commission_rate <= 1"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignmentConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssignmentCursor") - .HasColumnType("integer") - .HasColumnName("assignment_cursor"); - - b.Property("AssignmentMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("AssignmentPool") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_pool") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DelaySeconds") - .HasColumnType("integer") - .HasColumnName("delay_seconds"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("ExamType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("exam_type"); - - b.Property("FormName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("form_name"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Url") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("url"); - - b.HasKey("Id") - .HasName("pk_crm_config"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_config_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_crm_config_tenant_id"); - - b.HasIndex("TenantId", "AssignmentMode") - .HasDatabaseName("ix_crm_config_tenant_id_assignment_mode"); - - b.ToTable("crm_config", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempt") - .HasColumnType("integer") - .HasColumnName("attempt"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("HttpCode") - .HasColumnType("integer") - .HasColumnName("http_code"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Outcome") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("outcome"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("RequestBody") - .HasColumnType("text") - .HasColumnName("request_body"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ResponseSummary") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("response_summary"); - - b.Property("SignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("signed_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_log"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_log_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_log_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_log_tenant_id_record_id_created_at"); - - b.ToTable("crm_webhook_log", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("ScheduledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Source") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("target_url"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_queue"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_queue_tenant_id_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_idempotency_key") - .HasFilter("idempotency_key is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id_created_at") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "Source", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_source_created_at") - .HasFilter("source = 'tenant.student.crm_push'"); - - b.HasIndex("TenantId", "Status", "ScheduledAt", "NextAttemptAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_status_scheduled_at_next_attemp~"); - - b.ToTable("crm_webhook_queue", null, t => - { - t.HasCheckConstraint("ck_crm_webhook_queue_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LandingPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("landing_path"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_codes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_codes_user_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_code"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_user_id"); - - b.HasIndex("TenantId", "Code", "Status") - .HasDatabaseName("ix_referral_codes_tenant_id_code_status"); - - b.ToTable("referral_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("assigned_at"); - - b.Property("AssignedBy") - .HasColumnType("uuid") - .HasColumnName("assigned_by"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("AssignmentMode") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("BindType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bind_type"); - - b.Property("BoundAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("bound_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FirstTrackId") - .HasColumnType("uuid") - .HasColumnName("first_track_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ProtectedUntil") - .HasColumnType("timestamp with time zone") - .HasColumnName("protected_until"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_leads"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_leads_tenant_id_id"); - - b.HasIndex("AssignedBy") - .HasDatabaseName("ix_referral_leads_assigned_by"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_referral_leads_assigned_to_user_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_leads_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_referral_leads_student_user_id"); - - b.HasIndex("TenantId", "FirstTrackId") - .HasDatabaseName("ix_referral_leads_tenant_id_first_track_id"); - - b.HasIndex("TenantId", "StudentUserId") - .IsUnique() - .HasDatabaseName("ix_referral_leads_tenant_id_student_user_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_referrer_user_id_bound_at"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_assigned_to_user_id_status_bound_at"); - - b.ToTable("referral_leads", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Page") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("page"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("QrcodeUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("qrcode_url"); - - b.Property("RefCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("Scene") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("scene"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_qrcodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_qrcodes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_qrcodes_user_id"); - - b.HasIndex("TenantId", "RefCode", "Status") - .HasDatabaseName("ix_referral_qrcodes_tenant_id_ref_code_status"); - - b.HasIndex("TenantId", "Provider", "Scene", "Page") - .IsUnique() - .HasDatabaseName("ix_referral_qrcodes_tenant_id_provider_scene_page"); - - b.ToTable("referral_qrcodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LeaderUserId") - .HasColumnType("uuid") - .HasColumnName("leader_user_id"); - - b.Property("MemberUserId") - .HasColumnType("uuid") - .HasColumnName("member_user_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RelationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("relation_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_team_edges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_team_edges_tenant_id_id"); - - b.HasIndex("LeaderUserId") - .HasDatabaseName("ix_referral_team_edges_leader_user_id"); - - b.HasIndex("MemberUserId") - .HasDatabaseName("ix_referral_team_edges_member_user_id"); - - b.HasIndex("TenantId", "LeaderUserId", "Status") - .HasDatabaseName("ix_referral_team_edges_tenant_id_leader_user_id_status"); - - b.HasIndex("TenantId", "MemberUserId", "RelationType") - .IsUnique() - .HasDatabaseName("ix_referral_team_edges_tenant_id_member_user_id_relation_type"); - - b.ToTable("referral_team_edges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("LeadId") - .HasColumnType("uuid") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("TargetUserId") - .HasColumnType("uuid") - .HasColumnName("target_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_referral_tracks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_tracks_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_tracks_referrer_user_id"); - - b.HasIndex("TargetUserId") - .HasDatabaseName("ix_referral_tracks_target_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_referral_tracks_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "LeadId", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_lead_id_created_at"); - - b.HasIndex("TenantId", "RefCode", "EventType", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_ref_code_event_type_created_at"); - - b.ToTable("referral_tracks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("MinSettlementCents") - .HasColumnType("integer") - .HasColumnName("min_settlement_cents"); - - b.Property("SettlementCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("settlement_cycle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_commission_settings"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_commission_settings_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_commission_settings_tenant_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_commission_settings_updated_by"); - - b.ToTable("tenant_commission_settings", null, t => - { - t.HasCheckConstraint("ck_tenant_commission_settings_default_rate", "default_rate >= 0 and default_rate <= 1"); - - t.HasCheckConstraint("ck_tenant_commission_settings_min_settlement", "min_settlement_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastCheckInDate") - .HasColumnType("date") - .HasColumnName("last_check_in_date"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("MasteredWordsCount") - .HasColumnType("integer") - .HasColumnName("mastered_words_count"); - - b.Property("ModuleSelections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_selections") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Progress") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("progress") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionsAnsweredToday") - .HasColumnType("integer") - .HasColumnName("questions_answered_today"); - - b.Property("RecentActivities") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("recent_activities") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SelectedMajorId") - .HasColumnType("uuid") - .HasColumnName("selected_major_id"); - - b.Property("SelectedSchoolId") - .HasColumnType("uuid") - .HasColumnName("selected_school_id"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_student_profiles"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_student_profiles_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_student_profiles_user_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_student_profiles_tenant_id_region_id"); - - b.HasIndex("TenantId", "SelectedMajorId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_major_id"); - - b.HasIndex("TenantId", "SelectedSchoolId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_school_id"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_student_profiles_tenant_id_user_id"); - - b.ToTable("student_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AvatarUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("avatar_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("LastSeenAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPasswordHash") - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasColumnName("legacy_password_hash"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PasswordMigrationRequired") - .HasColumnType("boolean") - .HasColumnName("password_migration_required"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("PrimaryRole") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("primary_role"); - - b.Property("RawProfile") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_profile") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Score") - .HasColumnType("integer") - .HasColumnName("score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Username") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Email") - .IsUnique() - .HasDatabaseName("ix_users_email"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_users_legacy_id"); - - b.HasIndex("Phone") - .IsUnique() - .HasDatabaseName("ix_users_phone"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("OpenId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("open_id"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderSubject") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("provider_subject"); - - b.Property("SecretPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("secret_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UnionId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("union_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_identities"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_identities_user_id"); - - b.HasIndex("Provider", "ProviderSubject") - .IsUnique() - .HasDatabaseName("ix_user_identities_provider_provider_subject"); - - b.ToTable("user_identities", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("IssueCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("RawValueSample") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("raw_value_sample"); - - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_run_id"); - - b.HasIndex("RunId", "Severity", "CollectionName") - .HasDatabaseName("ix_pb_import_issues_run_id_severity_collection_name"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("SourceKind") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_kind"); - - b.Property("SourceName") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_runs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_runs_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "StartedAt") - .HasDatabaseName("ix_pb_import_runs_tenant_id_status_started_at"); - - b.ToTable("pb_import_runs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("CollectionName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Errors") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("errors") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ImportedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("imported_at") - .HasDefaultValueSql("now()"); - - b.Property("Normalized") - .HasColumnType("boolean") - .HasColumnName("normalized"); - - b.Property("Record") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("record") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("RunId", "CollectionName", "LegacyId") - .HasName("pk_pb_raw_records"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_run_id"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_raw_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("AnsweredAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("answered_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsCorrect") - .HasColumnType("boolean") - .HasColumnName("is_correct"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("QuestionVersionId") - .HasColumnType("uuid") - .HasColumnName("question_version_id"); - - b.Property("SelectedOptions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("selected_options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_answer_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_answer_records_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_answer_records_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_answer_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id"); - - b.HasIndex("TenantId", "QuestionId", "QuestionVersionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id_question_version_id"); - - b.HasIndex("TenantId", "UserId", "PracticeSessionId") - .HasDatabaseName("ix_answer_records_tenant_id_user_id_practice_session_id"); - - b.ToTable("answer_records", null, t => - { - t.HasCheckConstraint("ck_answer_records_version_requires_question", "question_version_id is null or question_id is not null"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActiveUsers") - .HasColumnType("integer") - .HasColumnName("active_users"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("NewOrders") - .HasColumnType("integer") - .HasColumnName("new_orders"); - - b.Property("NewQuestions") - .HasColumnType("integer") - .HasColumnName("new_questions"); - - b.Property("NewRevenueCents") - .HasColumnType("integer") - .HasColumnName("new_revenue_cents"); - - b.Property("NewUsers") - .HasColumnType("integer") - .HasColumnName("new_users"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_dashboard_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_dashboard_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_stat_date_region_id"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId"), false); - - b.ToTable("dashboard_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("ExamAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("exam_at"); - - b.Property("ExamName") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("exam_name"); - - b.Property("ExamType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("exam_type"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_exam_dates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_exam_dates_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_exam_dates_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_exam_dates_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId", "ExamAt") - .HasDatabaseName("ix_exam_dates_tenant_id_school_id_exam_at"); - - b.ToTable("exam_dates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_favorite_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_favorite_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_favorite_questions_tenant_id_question_id"); - - b.ToTable("favorite_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_mode"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementId") - .HasColumnType("uuid") - .HasColumnName("entitlement_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("GrantedCount") - .HasColumnType("integer") - .HasColumnName("granted_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("Reason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("reason"); - - b.Property("RequestedCount") - .HasColumnType("integer") - .HasColumnName("requested_count"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_access_events_user_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_access_events_tenant_id_practice_session_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_practice_access_events_tenant_id_user_id_created_at"); - - b.ToTable("practice_access_events", null, t => - { - t.HasCheckConstraint("ck_practice_access_events_consumed_free_quota", "consumed_free_quota >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_granted_count", "granted_count >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_requested_count", "requested_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FreeLimit") - .HasColumnType("integer") - .HasColumnName("free_limit"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsageDate") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasColumnName("usage_date") - .HasDefaultValueSql("current_date"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_daily_usage"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_daily_usage_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_daily_usage_user_id"); - - b.HasIndex("TenantId", "UserId", "UsageDate") - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date"); - - b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId") - .IsUnique() - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date_scope_typ~"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId"), false); - - b.ToTable("practice_daily_usage", null, t => - { - t.HasCheckConstraint("ck_practice_daily_usage_free_limit", "free_limit >= 0"); - - t.HasCheckConstraint("ck_practice_daily_usage_used_count", "used_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessEntitlementId") - .HasColumnType("uuid") - .HasColumnName("access_entitlement_id"); - - b.Property("AccessMode") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("free") - .HasColumnName("access_mode"); - - b.Property("AccessSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_sessions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "UserId", "Id") - .HasName("ak_practice_sessions_tenant_id_user_id_id"); - - 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"); - - b.ToTable("practice_sessions", null, t => - { - t.HasCheckConstraint("ck_practice_sessions_consumed_free_quota", "consumed_free_quota >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionResults") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_results") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionStats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("section_stats") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("SubmittedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("submitted_at") - .HasDefaultValueSql("now()"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalQuestions") - .HasColumnType("integer") - .HasColumnName("total_questions"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.Property("WrongQuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("wrong_question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.HasKey("Id") - .HasName("pk_practice_session_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_reports_tenant_id_id"); - - 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.ToTable("practice_session_reports", null, t => - { - t.HasCheckConstraint("ck_practice_session_reports_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_reports_counts", "total_questions >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_duration", "duration_seconds >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("question_type"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SectionName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("section_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_practice_session_report_sections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_report_sections_tenant_id_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_practice_session~"); - - b.HasIndex("TenantId", "ReportId", "SectionKey") - .IsUnique() - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sectio~"); - - b.HasIndex("TenantId", "ReportId", "SortOrder") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sort_o~"); - - b.ToTable("practice_session_report_sections", null, t => - { - t.HasCheckConstraint("ck_practice_session_report_sections_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_report_sections_counts", "question_count >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_report_sections_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastAccessAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_access_at"); - - b.Property("LastPracticeAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_practice_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("practice_type"); - - b.Property("Progress") - .HasColumnType("integer") - .HasColumnName("progress"); - - b.Property("TargetLegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("target_legacy_id"); - - b.Property("TargetName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("target_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_recent_practices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_recent_practices_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_recent_practices_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_recent_practices_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "LastAccessAt") - .HasDatabaseName("ix_recent_practices_tenant_id_user_id_last_access_at"); - - b.ToTable("recent_practices", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attachments") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("attachments") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("Contact") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("contact"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("HandledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("handled_at"); - - b.Property("HandledBy") - .HasColumnType("uuid") - .HasColumnName("handled_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Resolution") - .HasColumnType("text") - .HasColumnName("resolution"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_reports_tenant_id_id"); - - b.HasIndex("HandledBy") - .HasDatabaseName("ix_reports_handled_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_reports_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_reports_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_question_id_created_at") - .HasFilter("question_id is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_status_created_at"); - - b.ToTable("reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_report_status_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_report_status_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_report_status_events_actor_user_id"); - - b.HasIndex("TenantId", "ReportId", "CreatedAt") - .HasDatabaseName("ix_report_status_events_tenant_id_report_id_created_at"); - - b.ToTable("report_status_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CodeCount") - .HasColumnType("integer") - .HasColumnName("code_count"); - - b.Property("CodeEstimated") - .HasColumnType("integer") - .HasColumnName("code_estimated"); - - b.Property("CodeUsed") - .HasColumnType("integer") - .HasColumnName("code_used"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EstimatedRevenueCents") - .HasColumnType("integer") - .HasColumnName("estimated_revenue_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("OrderCount") - .HasColumnType("integer") - .HasColumnName("order_count"); - - b.Property("RealRevenueCents") - .HasColumnType("integer") - .HasColumnName("real_revenue_cents"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_revenue_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_revenue_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_stat_date_region_id_sale_type"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType"), false); - - b.ToTable("revenue_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceAfter") - .HasColumnType("integer") - .HasColumnName("balance_after"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Points") - .HasColumnType("integer") - .HasColumnName("points"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_score_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_score_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_score_events_user_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_user_score_events_tenant_id_idempotency_key"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_user_score_events_tenant_id_user_id_created_at"); - - b.ToTable("user_score_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("LastWrongAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_wrong_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("WrongCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(1) - .HasColumnName("wrong_count"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_wrong_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_wrong_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_wrong_questions_tenant_id_question_id"); - - b.ToTable("wrong_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_announcements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_announcements_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_announcements_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "IsActive", "SortOrder") - .HasDatabaseName("ix_announcements_tenant_id_is_active_sort_order"); - - b.ToTable("announcements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_audit_logs"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_audit_logs_actor_user_id"); - - b.HasIndex("TenantId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_created_at"); - - b.HasIndex("TenantId", "TargetType", "TargetId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_target_type_target_id_created_at"); - - b.ToTable("audit_logs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("ConditionExtra") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("condition_extra") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ConditionField") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("condition_field"); - - b.Property("ConditionOperator") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("condition_operator"); - - b.Property("ConditionValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("condition_value"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon_url"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Level") - .HasColumnType("integer") - .HasColumnName("level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnlockType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("unlock_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_badges_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsActive", "SortOrder") - .HasDatabaseName("ix_badges_tenant_id_category_is_active_sort_order"); - - b.ToTable("badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("BorderColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("border_color"); - - b.Property("ButtonLink") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("button_link"); - - b.Property("ButtonText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("button_text"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Subtitle") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("subtitle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_banners"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_banners_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_banners_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_banners_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("banners", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Answer") - .HasColumnType("text") - .HasColumnName("answer"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Question") - .HasColumnType("text") - .HasColumnName("question"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_faqs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_faqs_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_faqs_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_faqs_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("faqs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("AdoptionId") - .HasColumnType("uuid") - .HasColumnName("adoption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("ReadBy") - .HasColumnType("uuid") - .HasColumnName("read_by"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_content_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_content_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_content_notifications_created_by"); - - b.HasIndex("ReadBy") - .HasDatabaseName("ix_tenant_content_notifications_read_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_source_question_bank~"); - - b.HasIndex("TenantId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_notification_type_de~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "AdoptionId", "NotificationType", "Status") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_adoption_id_notifica~"); - - b.ToTable("tenant_content_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActivePublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ActiveTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("active_template_code"); - - b.Property("ActiveTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DraftPublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("draft_template_code"); - - b.Property("DraftTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftUpdatedBy") - .HasColumnType("uuid") - .HasColumnName("draft_updated_by"); - - b.Property("PublishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("published_at"); - - b.Property("PublishedBy") - .HasColumnType("uuid") - .HasColumnName("published_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_configs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_theme_configs_tenant_id_id"); - - b.HasIndex("ActiveTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_active_template_code"); - - b.HasIndex("DraftTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_draft_template_code"); - - b.HasIndex("DraftUpdatedBy") - .HasDatabaseName("ix_tenant_theme_configs_draft_updated_by"); - - b.HasIndex("PublishedBy") - .HasDatabaseName("ix_tenant_theme_configs_published_by"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_configs_tenant_id"); - - b.ToTable("tenant_theme_configs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PreviewImageUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_image_url"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_templates"); - - b.HasAlternateKey("Code") - .HasName("ak_tenant_theme_templates_code"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_templates_code"); - - b.HasIndex("Status", "SortOrder", "Code") - .HasDatabaseName("ix_tenant_theme_templates_status_sort_order_code"); - - b.ToTable("tenant_theme_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BadgeId") - .HasColumnType("uuid") - .HasColumnName("badge_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrantedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("granted_at"); - - b.Property("GrantedBy") - .HasColumnType("uuid") - .HasColumnName("granted_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_badges_tenant_id_id"); - - b.HasIndex("GrantedBy") - .HasDatabaseName("ix_user_badges_granted_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_badges_user_id"); - - b.HasIndex("TenantId", "BadgeId") - .HasDatabaseName("ix_user_badges_tenant_id_badge_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "BadgeId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_user_id_badge_id"); - - b.ToTable("user_badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_user_notifications_created_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_notifications_user_id"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .HasDatabaseName("ix_user_notifications_tenant_id_source_type_source_id"); - - b.HasIndex("TenantId", "UserId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_notification_type_dedu~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "UserId", "Status", "CreatedAt") - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_status_created_at"); - - b.ToTable("user_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("AcknowledgedBy") - .HasColumnType("uuid") - .HasColumnName("acknowledged_by"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("AuditLogId") - .HasColumnType("uuid") - .HasColumnName("audit_log_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FirstSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("first_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("LastSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("RuleId") - .HasColumnType("uuid") - .HasColumnName("rule_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alerts"); - - b.HasIndex("AcknowledgedBy") - .HasDatabaseName("ix_platform_audit_alerts_acknowledged_by"); - - b.HasIndex("AuditLogId") - .HasDatabaseName("ix_platform_audit_alerts_audit_log_id"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_platform_audit_alerts_resolved_by"); - - b.HasIndex("RuleId", "AuditLogId") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alerts_rule_id_audit_log_id"); - - b.HasIndex("Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_status_severity_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_tenant_id_status_created_at"); - - b.ToTable("platform_audit_alerts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("ActionPatterns") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("action_patterns") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("Conditions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("conditions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.PrimitiveCollection("TargetTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("target_types") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alert_rules"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alert_rules_code"); - - b.HasIndex("TenantId") - .HasDatabaseName("ix_platform_audit_alert_rules_tenant_id"); - - b.HasIndex("Enabled", "Severity", "Code") - .HasDatabaseName("ix_platform_audit_alert_rules_enabled_severity_code"); - - b.ToTable("platform_audit_alert_rules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationChannel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChannelCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel_code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MinReminderLevel") - .HasColumnType("integer") - .HasColumnName("min_reminder_level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.PrimitiveCollection("ReminderChannels") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_channels") - .HasDefaultValueSql("array['internal']::text[]"); - - b.PrimitiveCollection("ReminderTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_types") - .HasDefaultValueSql("array['overdue', 'final_notice']::text[]"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.PrimitiveCollection("TenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WebhookUrl") - .IsRequired() - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("webhook_url"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_channels"); - - b.HasIndex("ChannelCode") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_channels_channel_code"); - - b.HasIndex("Enabled", "MinReminderLevel", "ChannelCode") - .HasDatabaseName("ix_platform_dunning_notification_channels_enabled_min_reminder~"); - - b.ToTable("platform_dunning_notification_channels", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_channels_level", "min_reminder_level between 1 and 20"); - - t.HasCheckConstraint("ck_platform_dunning_channels_timeout", "timeout_seconds between 1 and 60"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("ChannelId") - .HasColumnType("uuid") - .HasColumnName("channel_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.Property("ReminderId") - .HasColumnType("uuid") - .HasColumnName("reminder_id"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScheduledAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at") - .HasDefaultValueSql("now()"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_platform_dunning_notification_events_tenant_id_id"); - - b.HasIndex("ChannelId", "ReminderId") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_events_channel_id_reminder_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_invoice_id"); - - b.HasIndex("TenantId", "ReminderId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_reminder_id"); - - b.HasIndex("InvoiceId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_invoice_id_status_crea~"); - - b.HasIndex("ReminderId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_reminder_id_status_cre~"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_status_creat~"); - - b.HasIndex("Status", "NextAttemptAt", "ScheduledAt", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_status_next_attempt_at~"); - - b.ToTable("platform_dunning_notification_events", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_events_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformSaasPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseAmountCents") - .HasColumnType("integer") - .HasColumnName("base_amount_cents"); - - b.Property("BillingCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_cycle"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IncludedQuotas") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("included_quotas") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OveragePrices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("overage_prices") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_saas_plans"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_saas_plans_code"); - - b.HasIndex("Status", "SortOrder") - .HasDatabaseName("ix_platform_saas_plans_status_sort_order"); - - b.ToTable("platform_saas_plans", null, t => - { - t.HasCheckConstraint("ck_platform_saas_plans_amount", "base_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BankAccountMasked") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("bank_account_masked"); - - b.Property("BankName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bank_name"); - - b.Property("BillingAddress") - .HasColumnType("text") - .HasColumnName("billing_address"); - - b.Property("BillingName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("billing_name"); - - b.Property("ContactEmail") - .HasMaxLength(300) - .HasColumnType("citext") - .HasColumnName("contact_email"); - - b.Property("ContactName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("contact_name"); - - b.Property("ContactPhone") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("contact_phone"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceTitle") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("invoice_title"); - - b.Property("InvoiceType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TaxId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("tax_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_billing_profiles"); - - b.ToTable("tenant_billing_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceCents") - .HasColumnType("integer") - .HasColumnName("balance_cents"); - - b.Property("BillingPeriodEnd") - .HasColumnType("date") - .HasColumnName("billing_period_end"); - - b.Property("BillingPeriodStart") - .HasColumnType("date") - .HasColumnName("billing_period_start"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("DiscountCents") - .HasColumnType("integer") - .HasColumnName("discount_cents"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("invoice_no"); - - b.Property("InvoiceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidCents") - .HasColumnType("integer") - .HasColumnName("paid_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubtotalCents") - .HasColumnType("integer") - .HasColumnName("subtotal_cents"); - - b.Property("TaxCents") - .HasColumnType("integer") - .HasColumnName("tax_cents"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCents") - .HasColumnType("integer") - .HasColumnName("total_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoices_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoices_created_by"); - - b.HasIndex("InvoiceNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_invoice_no"); - - b.HasIndex("TenantId", "BillingPeriodStart", "BillingPeriodEnd") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_tenant_id_billing_period_start_billing_peri~") - .HasFilter("invoice_type = 'usage_overage' and status <> 'void' and metadata->>'source' = 'usage_overage_auto'"); - - b.HasIndex("TenantId", "Status", "DueDate") - .HasDatabaseName("ix_tenant_invoices_tenant_id_status_due_date"); - - b.ToTable("tenant_invoices", null, t => - { - t.HasCheckConstraint("ck_tenant_invoices_amounts", "subtotal_cents >= 0 and discount_cents >= 0 and tax_cents >= 0 and total_cents >= 0 and paid_cents >= 0 and balance_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoices_period", "billing_period_end is null or billing_period_start is null or billing_period_end >= billing_period_start"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("description"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("ItemRefId") - .HasColumnType("uuid") - .HasColumnName("item_ref_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Quantity") - .HasPrecision(12, 2) - .HasColumnType("numeric(12,2)") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_items_tenant_id_id"); - - b.HasIndex("InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_invoice_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_items", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_items_amounts", "unit_amount_cents >= 0 and amount_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_no"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReceivedBy") - .HasColumnType("uuid") - .HasColumnName("received_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_payments_tenant_id_id"); - - b.HasIndex("PaymentNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_payments_payment_no"); - - b.HasIndex("ReceivedBy") - .HasDatabaseName("ix_tenant_invoice_payments_received_by"); - - b.HasIndex("InvoiceId", "Status") - .HasDatabaseName("ix_tenant_invoice_payments_invoice_id_status"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_payments_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_payments", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("BalanceCentsSnapshot") - .HasColumnType("integer") - .HasColumnName("balance_cents_snapshot"); - - b.Property("Channel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("channel"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Message") - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReminderDate") - .HasColumnType("date") - .HasColumnName("reminder_date"); - - b.Property("ReminderLevel") - .HasColumnType("integer") - .HasColumnName("reminder_level"); - - b.Property("ReminderType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("reminder_type"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_reminders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_reminders_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoice_reminders_created_by"); - - b.HasIndex("InvoiceId", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_invoice_id_reminder_date"); - - b.HasIndex("TenantId", "Status", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_status_reminder_date"); - - b.HasIndex("TenantId", "InvoiceId", "ReminderType", "Channel", "ReminderDate") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_invoice_id_reminder_type~"); - - b.ToTable("tenant_invoice_reminders", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_reminders_balance", "balance_cents_snapshot >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_reminders_level", "reminder_level between 1 and 20"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CurrentVersionId") - .HasColumnType("uuid") - .HasColumnName("current_version_id"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExamMarkers") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("exam_markers") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("HasVideoExplanation") - .HasColumnType("boolean") - .HasColumnName("has_video_explanation"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyNodeId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_node_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("MediaUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("media_url"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PrimaryCollectionId") - .HasColumnType("uuid") - .HasColumnName("primary_collection_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("TypeLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("type_label"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_questions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_questions_tenant_id_id"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_questions_tenant_id_category_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_questions_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_questions_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_questions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_questions_tenant_id_node_id"); - - b.HasIndex("TenantId", "PrimaryCollectionId") - .HasDatabaseName("ix_questions_tenant_id_primary_collection_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_questions_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_questions_tenant_id_subject_id"); - - b.HasIndex("TenantId", "Id", "CurrentVersionId") - .HasDatabaseName("ix_questions_tenant_id_id_current_version_id"); - - b.ToTable("questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SourceScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_scope"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_banks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_banks_tenant_id_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_banks_tenant_id_region_id"); - - b.ToTable("question_banks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("CodeLang") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code_lang"); - - b.Property("CodeTemplate") - .HasColumnType("text") - .HasColumnName("code_template"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CorrectOptionIndex") - .HasColumnType("integer") - .HasColumnName("correct_option_index"); - - b.Property("CorrectOptionIndices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("correct_option_indices") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Explanation") - .HasColumnType("text") - .HasColumnName("explanation"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubQuestions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sub_questions") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("VersionNo") - .HasColumnType("integer") - .HasColumnName("version_no"); - - b.HasKey("Id") - .HasName("pk_question_versions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_versions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "QuestionId", "Id") - .HasName("ak_question_versions_tenant_id_question_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_versions_created_by"); - - b.HasIndex("QuestionId", "VersionNo") - .IsUnique() - .HasDatabaseName("ix_question_versions_question_id_version_no"); - - b.ToTable("question_versions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FailureCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("failure_code"); - - b.Property("Identifier") - .HasMaxLength(320) - .HasColumnType("character varying(320)") - .HasColumnName("identifier"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_login_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_login_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_login_events_user_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_auth_login_events_tenant_id_user_id_created_at"); - - b.ToTable("auth_login_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TokenHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("token_hash"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_sessions_tenant_id_id"); - - b.HasIndex("TokenHash") - .IsUnique() - .HasDatabaseName("ix_auth_sessions_token_hash"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_sessions_user_id"); - - b.HasIndex("TenantId", "UserId", "ExpiresAt") - .HasDatabaseName("ix_auth_sessions_tenant_id_user_id_expires_at") - .HasFilter("revoked_at is null"); - - b.ToTable("auth_sessions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Dimension") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("dimension"); - - b.Property("ScopeHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("scope_hash"); - - b.Property("BucketStart") - .HasColumnType("timestamp with time zone") - .HasColumnName("bucket_start"); - - b.Property("RequestCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(0) - .HasColumnName("request_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId", "Dimension", "ScopeHash", "BucketStart") - .HasName("pk_sms_send_rate_limits"); - - b.HasIndex("UpdatedAt") - .HasDatabaseName("ix_sms_send_rate_limits_updated_at"); - - b.ToTable("sms_send_rate_limits", null, t => - { - t.HasCheckConstraint("ck_sms_send_rate_limits_request_count", "request_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CodeHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("code_hash"); - - b.Property("ConsumedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("consumed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phone") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Purpose") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("purpose"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_sms_verification_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_sms_verification_codes_tenant_id_id"); - - b.HasIndex("TenantId", "Phone", "Purpose") - .IsUnique() - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "CreatedAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_created_at") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "ExpiresAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_expires_at"); - - b.ToTable("sms_verification_codes", null, t => - { - t.HasCheckConstraint("ck_sms_verification_codes_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BillingStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_status"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegalName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("legal_name"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OwnerUserId") - .HasColumnType("uuid") - .HasColumnName("owner_user_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("slug"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenants"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenants_legacy_id"); - - b.HasIndex("OwnerUserId") - .HasDatabaseName("ix_tenants_owner_user_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_tenants_slug"); - - b.ToTable("tenants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_auth_providers"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_auth_providers_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider"); - - b.HasIndex("TenantId", "Provider", "Status") - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider_status"); - - b.ToTable("tenant_auth_providers", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BrandName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("brand_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FaviconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("favicon_url"); - - b.Property("LogoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("logo_url"); - - b.Property("OrganizationName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("organization_name"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ServiceAccountName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("service_account_name"); - - b.Property("ServiceWechat") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("service_wechat"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("Slogan") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("slogan"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_branding"); - - b.ToTable("tenant_branding", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_classes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_classes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_classes_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_classes_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_code") - .HasFilter("code is not null and code <> ''"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_tenant_classes_tenant_id_region_id") - .HasFilter("region_id is not null"); - - b.HasIndex("TenantId", "Status", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_tenant_classes_tenant_id_status_sort_order_created_at"); - - b.ToTable("tenant_classes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("JoinedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("joined_at") - .HasDefaultValueSql("now()"); - - b.Property("LeftAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("left_at"); - - b.Property("MemberType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("member_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_class_members"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_class_members_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_class_members_user_id"); - - b.HasIndex("TenantId", "ClassId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_status_member_type"); - - b.HasIndex("TenantId", "ClassId", "UserId", "MemberType") - .IsUnique() - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_user_id_member_type"); - - b.HasIndex("TenantId", "UserId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_user_id_status_member_type"); - - b.ToTable("tenant_class_members", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DomainType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("domain_type"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(253) - .HasColumnType("citext") - .HasColumnName("host"); - - b.Property("IsPrimary") - .HasColumnType("boolean") - .HasColumnName("is_primary"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VerificationToken") - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("verification_token"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.HasKey("Id") - .HasName("pk_tenant_domains"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_domains_tenant_id_id"); - - b.HasIndex("Host") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_host"); - - b.HasIndex("TenantId", "IsPrimary") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_tenant_id_is_primary") - .HasFilter("is_primary"); - - b.ToTable("tenant_domains", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyRole") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("legacy_role"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Role") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("role"); - - b.Property("RoleTemplateId") - .HasColumnType("uuid") - .HasColumnName("role_template_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_memberships"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_memberships_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_memberships_user_id"); - - b.HasIndex("TenantId", "RoleTemplateId") - .HasDatabaseName("ix_tenant_memberships_tenant_id_role_template_id"); - - b.HasIndex("TenantId", "UserId", "Role") - .IsUnique() - .HasDatabaseName("ix_tenant_memberships_tenant_id_user_id_role"); - - b.ToTable("tenant_memberships", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("base_role"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DataScope") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("data_scope") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FieldPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IsSystem") - .HasColumnType("boolean") - .HasColumnName("is_system"); - - b.Property("MenuPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("menu_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModulePermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_role_templates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_role_templates_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_role_templates_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_role_templates_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_role_templates_tenant_id_code"); - - b.HasIndex("TenantId", "Status", "SortOrder") - .HasDatabaseName("ix_tenant_role_templates_tenant_id_status_sort_order"); - - b.ToTable("tenant_role_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("AdminFeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("admin_feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PublicConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_settings"); - - b.ToTable("tenant_settings", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CompletedBy") - .HasColumnType("uuid") - .HasColumnName("completed_by"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("FollowupType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("followup_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_student_followups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_followups_tenant_id_id"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_tenant_student_followups_assigned_to_user_id"); - - b.HasIndex("CompletedBy") - .HasDatabaseName("ix_tenant_student_followups_completed_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_followups_student_user_id"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_assigned_to_user_id_stat~") - .HasFilter("assigned_to_user_id is not null"); - - b.HasIndex("TenantId", "ClassId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_class_id_status_due_at") - .HasFilter("class_id is not null"); - - b.HasIndex("TenantId", "StudentUserId", "Status", "DueAt", "CreatedAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_student_user_id_status_d~"); - - b.ToTable("tenant_student_followups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("IsPinned") - .HasColumnType("boolean") - .HasColumnName("is_pinned"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NoteType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("note_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_tenant_student_notes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_notes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_student_notes_created_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_notes_student_user_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_student_notes_updated_by"); - - b.HasIndex("TenantId", "CreatedBy", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_created_by_created_at") - .HasFilter("created_by is not null"); - - b.HasIndex("TenantId", "StudentUserId", "IsPinned", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_student_user_id_is_pinned_cr~"); - - b.ToTable("tenant_student_notes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_categories_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_majors_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_module_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_module_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_regions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_region_modules_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_region_modules_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_schools_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AgentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_agent_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_activation_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UsedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_used_by"); - - b.HasOne("Tiku.Domain.Commerce.CodeBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_code_batches_tenant_id_batch_id"); - - b.HasOne("Tiku.Domain.Commerce.CouponRedemption", null) - .WithMany() - .HasForeignKey("TenantId", "CouponRedemptionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_coupon_redemptions_tenant_id_coupon_redemp~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "UsedRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_regions_tenant_id_used_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_code_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_batches_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedTo") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_assigned_to"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_resolved_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_batc~"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_item~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_refund_requests_ten~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issue_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationIssue", null) - .WithMany() - .HasForeignKey("TenantId", "IssueId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_commerce_reconciliatio~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_commerce_reconciliation_batch~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_commerce_refund_requests_tena~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_commerce_refund_requests_tenant_id_r~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ProcessedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_processed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RequestedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_requested_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_refund_requests_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupons_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_coupons_svip_plans_tenant_id_plan_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupon_redemptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.Coupon", null) - .WithMany() - .HasForeignKey("TenantId", "CouponId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_coupons_tenant_id_coupon_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RevokedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_entitlements_users_revoked_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_orders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_orders_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payment_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_payment_events_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_products_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_products_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_svip_plans_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_svip_plans_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_payment_accounts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_subscriptions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_usage_records_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_ai_recommendation_reports_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_app_assets_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_assets_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("VerifiedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_verified_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_content_nodes_tenant_id_content_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_asset_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_access_events_content_assets_tenant_id_asset_~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_security_scan_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_security_scan_events_content_assets_tenant_id~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_entries_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_import_issues_content_import_items_tenant_id_item_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_import_jobs_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_jobs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_categories_tenant_id_target_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "TargetContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_content_nodes_tenant_id_target_content_~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_question_banks_tenant_id_target_questio~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "TargetRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_regions_tenant_id_target_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_nodes_content_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_chapters_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_chapters_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_handbook_chapters_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.HandbookSubject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_chapters_handbook_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.HandbookChapter", null) - .WithMany() - .HasForeignKey("TenantId", "ChapterId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_entries_handbook_chapters_tenant_id_chapter_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_entries_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_handbook_entries_content_entries_tenant_id_entry_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_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_handbook_subjects_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_images_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_blueprints_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_question_collections_tenant_id_collecti~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_bank_grants_question_banks_source_question_bank_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_question_banks_tenant_id_question_bank~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_question_collections_tenant_id_co~"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_type_groups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_type_groups_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_videos_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.Content.VideoExplanation", null) - .WithMany() - .HasForeignKey("TenantId", "VideoId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_video_explanations_tenant_id_video_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SourceSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_source_subject_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_created_by"); - - b.HasOne("Tiku.Domain.Content.QuestionBankGrant", null) - .WithMany() - .HasForeignKey("GrantId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_bank_grants_grant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_source_questi~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_collections_tenant_~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "TargetEntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_content_entries_tenant_id_ta~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_tenant_id_tar~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_video_explanations_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_video_explanations_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_units_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_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_vocabulary_units_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_words_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_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_vocabulary_words_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyUnit", null) - .WithMany() - .HasForeignKey("TenantId", "UnitId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_vocabulary_units_tenant_id_unit_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GeneratedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_generated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PaidBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_paid_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.CommissionSettlement", null) - .WithMany() - .HasForeignKey("TenantId", "SettlementId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_commission_settlements_tenant_i~"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_config_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_log_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_queue_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralTrack", null) - .WithMany() - .HasForeignKey("TenantId", "FirstTrackId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_referral_tracks_tenant_id_first_track_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_qrcodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_qrcodes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("LeaderUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_team_edges_users_leader_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("MemberUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_users_member_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("TargetUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_target_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_tracks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralLead", null) - .WithMany() - .HasForeignKey("TenantId", "LeadId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_referral_leads_tenant_id_lead_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_commission_settings_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_commission_settings_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedMajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_majors_tenant_id_selected_major_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedSchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_schools_tenant_id_selected_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_identities_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_pb_import_issues_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_runs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId", "QuestionVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_question_versions_tenant_id_question_id_ques~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "UserId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "UserId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_practice_sessions_tenant_id_user_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_dashboard_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_dashboard_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_exam_dates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_practice_sessions_tenant_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .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.PracticeSessionReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .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") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_practice_sessions_tenant_id_practi~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_sessions_tenant_i~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSessionReport", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_session_reports_t~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_recent_practices_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_recent_practices_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("HandledBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_handled_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_reports_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_report_status_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.Report", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_reports_tenant_id_report_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_revenue_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_revenue_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_announcements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_audit_logs_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_audit_logs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_badges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_banners_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_banners_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_faqs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_faqs_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReadBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_read_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_content_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "SourceQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_tenant_content_notifications_question_banks_tenant_id_sourc~"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("ActiveTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_active_template~"); - - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("DraftTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_draft_template_~"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("DraftUpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_draft_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PublishedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_published_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_theme_configs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GrantedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_badges_users_granted_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_badges_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_users_user_id"); - - b.HasOne("Tiku.Domain.Operations.Badge", null) - .WithMany() - .HasForeignKey("TenantId", "BadgeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_badges_tenant_id_badge_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AcknowledgedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_acknowledged_by"); - - b.HasOne("Tiku.Domain.Operations.AuditLog", null) - .WithMany() - .HasForeignKey("AuditLogId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_audit_logs_audit_log_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_resolved_by"); - - b.HasOne("Tiku.Domain.Platform.PlatformAuditAlertRule", null) - .WithMany() - .HasForeignKey("RuleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_platform_audit_alert_rules_rule_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alerts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alert_rules_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.HasOne("Tiku.Domain.Platform.PlatformDunningNotificationChannel", null) - .WithMany() - .HasForeignKey("ChannelId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_platform_dunning_notif~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoices_tenant~"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoiceReminder", null) - .WithMany() - .HasForeignKey("TenantId", "ReminderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoice_reminde~"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Platform.TenantBillingProfile", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_billing_profiles_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoices_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoices_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReceivedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_payments_users_received_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_reminders_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenant_invoices_tenant_id_invoice_~"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_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_questions_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "PrimaryCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_collections_tenant_id_primary_collection~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_banks_tenant_id_question_bank_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_subjects_tenant_id_subject_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "Id", "CurrentVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_versions_tenant_id_id_current_version_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_banks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_banks_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_versions_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_versions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_login_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_auth_login_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_send_rate_limits_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_verification_codes_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("OwnerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenants_users_owner_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_auth_providers_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantBranding", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_branding_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_classes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_classes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_domains_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantRoleTemplate", null) - .WithMany() - .HasForeignKey("TenantId", "RoleTemplateId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_role_templates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantSettings", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_settings_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CompletedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_completed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_updated_by"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726071027_AddProductActiveAndOperationsCatalog.Designer.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726071027_AddProductActiveAndOperationsCatalog.Designer.cs deleted file mode 100644 index 192772e..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726071027_AddProductActiveAndOperationsCatalog.Designer.cs +++ /dev/null @@ -1,15308 +0,0 @@ -// -using System; -using System.Text.Json; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using Tiku.Infrastructure.Persistence; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - [DbContext(typeof(TikuDbContext))] - [Migration("20260726071027_AddProductActiveAndOperationsCatalog")] - partial class AddProductActiveAndOperationsCatalog - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext"); - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "ltree"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("category_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("SvipQuestionLimit") - .HasColumnType("integer") - .HasColumnName("svip_question_limit"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_categories"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_categories_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_categories_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_categories_tenant_id_node_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_categories_tenant_id_subject_id"); - - b.ToTable("categories", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("StudyTips") - .HasColumnType("text") - .HasColumnName("study_tips"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_majors"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_majors_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_majors_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_majors_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_majors_tenant_id_school_id"); - - b.ToTable("majors", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyModuleId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_module_id"); - - b.Property("LegacyParentId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_parent_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_module_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_module_nodes_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_module_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_module_nodes_tenant_id_module_id"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_module_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_module_nodes_tenant_id_region_id"); - - b.ToTable("module_nodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FullName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("full_name"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsHot") - .HasColumnType("boolean") - .HasColumnName("is_hot"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Pinyin") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("pinyin"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_regions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_regions_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_regions_tenant_id_legacy_id"); - - b.ToTable("regions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsPrimarySchoolModule") - .HasColumnType("boolean") - .HasColumnName("is_primary_school_module"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TextColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("text_color"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_region_modules"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_region_modules_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_region_modules_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_region_modules_tenant_id_region_id"); - - b.ToTable("region_modules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ProfessionalExamDate") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("professional_exam_date"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_schools"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_schools_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_schools_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_schools_tenant_id_module_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_schools_tenant_id_region_id"); - - b.ToTable("schools", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_subjects_tenant_id_module_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_subjects_tenant_id_node_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_subjects_tenant_id_school_id"); - - b.ToTable("subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AgentUserId") - .HasColumnType("uuid") - .HasColumnName("agent_user_id"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponRedemptionId") - .HasColumnType("uuid") - .HasColumnName("coupon_redemption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("IsUsed") - .HasColumnType("boolean") - .HasColumnName("is_used"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("SoldTo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("sold_to"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitPriceCents") - .HasColumnType("integer") - .HasColumnName("unit_price_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UsedBy") - .HasColumnType("uuid") - .HasColumnName("used_by"); - - b.Property("UsedRegionId") - .HasColumnType("uuid") - .HasColumnName("used_region_id"); - - b.HasKey("Id") - .HasName("pk_activation_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_activation_codes_tenant_id_id"); - - b.HasIndex("AgentUserId") - .HasDatabaseName("ix_activation_codes_agent_user_id"); - - b.HasIndex("UsedBy") - .HasDatabaseName("ix_activation_codes_used_by"); - - b.HasIndex("TenantId", "BatchId") - .HasDatabaseName("ix_activation_codes_tenant_id_batch_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_code"); - - b.HasIndex("TenantId", "CouponRedemptionId") - .HasDatabaseName("ix_activation_codes_tenant_id_coupon_redemption_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UsedRegionId") - .HasDatabaseName("ix_activation_codes_tenant_id_used_region_id"); - - b.ToTable("activation_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CampaignName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("campaign_name"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CostPriceCents") - .HasColumnType("integer") - .HasColumnName("cost_price_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("DefaultUnitPriceCents") - .HasColumnType("integer") - .HasColumnName("default_unit_price_cents"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_code_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_code_batches_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_code_batches_tenant_id_legacy_id"); - - b.ToTable("code_batches", null, t => - { - t.HasCheckConstraint("ck_code_batches_amounts", "default_unit_price_cents >= 0 and cost_price_cents >= 0"); - - t.HasCheckConstraint("ck_code_batches_counts", "total_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillDate") - .HasColumnType("date") - .HasColumnName("bill_date"); - - b.Property("BillType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bill_type"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DuplicateCount") - .HasColumnType("integer") - .HasColumnName("duplicate_count"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IgnoredCount") - .HasColumnType("integer") - .HasColumnName("ignored_count"); - - b.Property("MatchedCount") - .HasColumnType("integer") - .HasColumnName("matched_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MismatchCount") - .HasColumnType("integer") - .HasColumnName("mismatch_count"); - - b.Property("MissingLocalCount") - .HasColumnType("integer") - .HasColumnName("missing_local_count"); - - b.Property("MissingProviderCount") - .HasColumnType("integer") - .HasColumnName("missing_provider_count"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source"); - - b.Property("SourceHash") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("source_name"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_batches_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_batches_created_by"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "Provider", "BillDate", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_provider_bill_dat~"); - - b.ToTable("commerce_reconciliation_batches", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_batches_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_batches_counts", "total_count >= 0 and matched_count >= 0 and mismatch_count >= 0 and missing_local_count >= 0 and missing_provider_count >= 0 and duplicate_count >= 0 and ignored_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("AssignedTo") - .HasColumnType("uuid") - .HasColumnName("assigned_to"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("IssueNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_no"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolutionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("resolution_type"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("summary"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issues_tenant_id_id"); - - b.HasIndex("AssignedTo") - .HasDatabaseName("ix_commerce_reconciliation_issues_assigned_to"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_created_by"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_resolved_by"); - - b.HasIndex("TenantId", "IssueNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_issue_no"); - - b.HasIndex("TenantId", "ItemId") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_item_id") - .HasFilter("item_id is not null and status in ('open', 'investigating', 'escalated')"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "PaymentId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_payment_id_created~"); - - b.HasIndex("TenantId", "AssignedTo", "Status", "DueAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_assigned_to_status~"); - - b.HasIndex("TenantId", "BatchId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_batch_id_status_cr~"); - - b.HasIndex("TenantId", "Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_status_severity_cr~"); - - b.ToTable("commerce_reconciliation_issues", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_issues_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("IssueId") - .HasColumnType("uuid") - .HasColumnName("issue_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issue_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issue_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_actor_user_id"); - - b.HasIndex("TenantId", "IssueId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_tenant_id_issue_id_cre~"); - - b.ToTable("commerce_reconciliation_issue_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LocalStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("local_status"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider_status"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("RefundedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("refunded_at"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_items_tenant_id_id"); - - b.HasIndex("BatchId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_items_batch_id_row_no"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "BatchId", "MatchStatus", "RowNo") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_batch_id_match_stat~"); - - b.HasIndex("TenantId", "Provider", "ProviderTradeNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_provider_provider_t~"); - - b.HasIndex("TenantId", "RefundNo", "ProviderRefundNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_no_provider_~"); - - b.ToTable("commerce_reconciliation_items", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_items_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_items_row", "row_no > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_refund_events_actor_user_id"); - - b.HasIndex("TenantId", "RefundRequestId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_events_tenant_id_refund_request_id_created_~"); - - b.ToTable("commerce_refund_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CancelledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("cancelled_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementAction") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entitlement_action"); - - b.Property("FailedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("failed_at"); - - b.Property("FailureReason") - .HasColumnType("text") - .HasColumnName("failure_reason"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("ProcessedBy") - .HasColumnType("uuid") - .HasColumnName("processed_by"); - - b.Property("Provider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("Reason") - .HasColumnType("text") - .HasColumnName("reason"); - - b.Property("RefundNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RequestedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("requested_at") - .HasDefaultValueSql("now()"); - - b.Property("RequestedBy") - .HasColumnType("uuid") - .HasColumnName("requested_by"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SucceededAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("succeeded_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_requests"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_requests_tenant_id_id"); - - b.HasIndex("ProcessedBy") - .HasDatabaseName("ix_commerce_refund_requests_processed_by"); - - b.HasIndex("RequestedBy") - .HasDatabaseName("ix_commerce_refund_requests_requested_by"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commerce_refund_requests_reviewed_by"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundNo") - .IsUnique() - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_refund_no"); - - b.HasIndex("TenantId", "OrderId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_order_id_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_status_created_at"); - - b.ToTable("commerce_refund_requests", null, t => - { - t.HasCheckConstraint("ck_commerce_refund_requests_amount", "amount_cents > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("discount_type"); - - b.Property("DiscountValue") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("discount_value"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MaxUses") - .HasColumnType("integer") - .HasColumnName("max_uses"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("ValidFrom") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_from"); - - b.Property("ValidTo") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_to"); - - b.HasKey("Id") - .HasName("pk_coupons"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupons_tenant_id_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_code"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupons_tenant_id_plan_id"); - - b.ToTable("coupons", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClaimedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("claimed_at"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponId") - .HasColumnType("uuid") - .HasColumnName("coupon_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountAppliedCents") - .HasColumnType("integer") - .HasColumnName("discount_applied_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_coupon_redemptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupon_redemptions_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_coupon_redemptions_user_id"); - - b.HasIndex("TenantId", "CouponId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_coupon_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_region_id"); - - b.HasIndex("TenantId", "OrderId", "UserId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_order_id_user_id") - .HasFilter("order_id is not null"); - - b.HasIndex("TenantId", "UserId", "CouponId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_user_id_coupon_id") - .HasFilter("coupon_id is not null"); - - b.ToTable("coupon_redemptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("entitlement_type"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("LegacySourceId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_source_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("RevokedBy") - .HasColumnType("uuid") - .HasColumnName("revoked_by"); - - b.Property("RevokedReason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("revoked_reason"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("StartsAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at") - .HasDefaultValueSql("now()"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_entitlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_entitlements_tenant_id_id"); - - b.HasIndex("RevokedBy") - .HasDatabaseName("ix_entitlements_revoked_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_entitlements_user_id"); - - b.HasIndex("TenantId", "UserId", "Status", "ExpiresAt") - .HasDatabaseName("ix_entitlements_tenant_id_user_id_status_expires_at"); - - b.ToTable("entitlements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPlanId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_plan_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("OrderNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PayMethod") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_method"); - - b.Property("PayProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_provider"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("ProductName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("product_name"); - - b.Property("ProductType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("product_type"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("trade_no"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_orders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_orders_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_orders_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderNo") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_order_no"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_orders_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_orders_tenant_id_region_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_orders_tenant_id_user_id_created_at"); - - b.ToTable("orders", null, t => - { - t.HasCheckConstraint("ck_orders_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_orders_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("Quantity") - .HasColumnType("integer") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalAmountCents") - .HasColumnType("integer") - .HasColumnName("total_amount_cents"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_order_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_order_items_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_order_items_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_order_items_tenant_id_order_id"); - - b.ToTable("order_items", null, t => - { - t.HasCheckConstraint("ck_order_items_amounts", "unit_amount_cents >= 0 and total_amount_cents >= 0"); - - t.HasCheckConstraint("ck_order_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyOrderId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_order_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payments_tenant_id_id"); - - b.HasIndex("Provider", "ProviderTradeNo") - .IsUnique() - .HasDatabaseName("ix_payments_provider_provider_trade_no") - .HasFilter("provider_trade_no is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_payments_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId", "UpdatedAt") - .HasDatabaseName("ix_payments_tenant_id_order_id_updated_at"); - - b.ToTable("payments", null, t => - { - t.HasCheckConstraint("ck_payments_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_payments_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("EventId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("event_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("SignatureValid") - .HasColumnType("boolean") - .HasColumnName("signature_valid"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_payment_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payment_events_tenant_id_id"); - - b.HasIndex("Provider", "EventId") - .IsUnique() - .HasDatabaseName("ix_payment_events_provider_event_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_payment_events_tenant_id_payment_id"); - - b.ToTable("payment_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Cover") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cover"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DetailImages") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("detail_images") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true) - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPriceText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_price_text"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("PreviewIframe") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_iframe"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_products"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_products_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_products_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "Type", "IsActive", "SortOrder") - .HasDatabaseName("ix_products_tenant_id_region_id_type_is_active_sort_order"); - - b.ToTable("products", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Badge") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("badge"); - - b.Property("CouponOnly") - .HasColumnType("boolean") - .HasColumnName("coupon_only"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OriginalPriceCents") - .HasColumnType("integer") - .HasColumnName("original_price_cents"); - - b.Property("PerDayLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("per_day_label"); - - b.Property("PriceCents") - .HasColumnType("integer") - .HasColumnName("price_cents"); - - b.Property("Recommended") - .HasColumnType("boolean") - .HasColumnName("recommended"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VpEnabled") - .HasColumnType("boolean") - .HasColumnName("vp_enabled"); - - b.Property("VpProductId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("vp_product_id"); - - b.HasKey("Id") - .HasName("pk_svip_plans"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_svip_plans_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_svip_plans_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_svip_plans_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("svip_plans", null, t => - { - t.HasCheckConstraint("ck_svip_plans_days", "days >= 0"); - - t.HasCheckConstraint("ck_svip_plans_price", "price_cents >= 0 and (original_price_cents is null or original_price_cents >= 0)"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_payment_accounts"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_payment_accounts_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_payment_accounts_tenant_id_provider"); - - b.ToTable("tenant_payment_accounts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillingCycle") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("billing_cycle"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PlanCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("plan_code"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_subscriptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_subscriptions_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "ExpiresAt") - .HasDatabaseName("ix_tenant_subscriptions_tenant_id_status_expires_at"); - - b.ToTable("tenant_subscriptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MetricKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("metric_key"); - - b.Property("MetricValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("metric_value"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_tenant_usage_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_usage_records_tenant_id_id"); - - b.HasIndex("TenantId", "MetricKey", "PeriodStart", "PeriodEnd") - .HasDatabaseName("ix_tenant_usage_records_tenant_id_metric_key_period_start_peri~"); - - b.ToTable("tenant_usage_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContextPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("context_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("GeneratedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("generated_at"); - - b.Property("InputPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("input_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Model") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("model"); - - b.Property("PromptVersion") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("prompt_version"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("ResultPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("result_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_ai_recommendation_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_ai_recommendation_reports_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_ai_recommendation_reports_user_id"); - - b.HasIndex("TenantId", "RegionId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_region_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_user_id_created_at"); - - b.ToTable("ai_recommendation_reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetKey") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_app_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_app_assets_tenant_id_id"); - - b.HasIndex("TenantId", "AssetKey") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_asset_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_legacy_id"); - - b.ToTable("app_assets", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssetKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("AssetType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("asset_type"); - - b.Property("Bucket") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bucket"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("ChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("checksum_sha256"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DownloadCount") - .HasColumnType("integer") - .HasColumnName("download_count"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("FileSizeBytes") - .HasColumnType("bigint") - .HasColumnName("file_size_bytes"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MimeType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mime_type"); - - b.Property("ObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("object_key"); - - b.Property("PreviewObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("preview_object_key"); - - b.Property("PreviewStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("preview_status"); - - b.Property("PreviewUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_url"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SecurityFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScanProvider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("security_scan_provider"); - - b.Property("SecurityScanStatus") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("not_required") - .HasColumnName("security_scan_status"); - - b.Property("SecurityScanSummary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_scan_summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScannedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("security_scanned_at"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StorageProvider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("storage_provider"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UploadStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("upload_status"); - - b.Property("VerificationDetails") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("verification_details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.Property("VerifiedBy") - .HasColumnType("uuid") - .HasColumnName("verified_by"); - - b.Property("VerifiedChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("verified_checksum_sha256"); - - b.Property("VerifiedSizeBytes") - .HasColumnType("bigint") - .HasColumnName("verified_size_bytes"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_assets_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_assets_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_assets_updated_by"); - - b.HasIndex("VerifiedBy") - .HasDatabaseName("ix_content_assets_verified_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_content_assets_tenant_id_category_id"); - - b.HasIndex("TenantId", "ChecksumSha256") - .HasDatabaseName("ix_content_assets_tenant_id_checksum_sha256") - .HasFilter("checksum_sha256 is not null"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_content_assets_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_assets_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PreviewStatus") - .HasDatabaseName("ix_content_assets_tenant_id_preview_status") - .HasFilter("preview_status <> 'none'"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_assets_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_content_assets_tenant_id_subject_id"); - - b.HasIndex("TenantId", "StorageProvider", "Bucket", "ObjectKey") - .HasDatabaseName("ix_content_assets_tenant_id_storage_provider_bucket_object_key"); - - b.HasIndex("TenantId", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_upload_status_status_updated_at"); - - b.HasIndex("TenantId", "SecurityScanStatus", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_security_scan_status_upload_status~"); - - b.HasIndex("TenantId", "Status", "Visibility", "AssetType", "RegionId", "SubjectId", "CategoryId", "SortOrder") - .HasDatabaseName("ix_content_assets_tenant_id_status_visibility_asset_type_regio~"); - - b.ToTable("content_assets", null, t => - { - t.HasCheckConstraint("ck_content_assets_download_count", "download_count >= 0"); - - t.HasCheckConstraint("ck_content_assets_file_size", "file_size_bytes is null or file_size_bytes >= 0"); - - t.HasCheckConstraint("ck_content_assets_verified_checksum", "verified_checksum_sha256 is null or verified_checksum_sha256 ~ '^[a-f0-9]{64}$'"); - - t.HasCheckConstraint("ck_content_assets_verified_size", "verified_size_bytes is null or verified_size_bytes >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_type"); - - b.Property("ActorRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("actor_role"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("AssetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("asset_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DenyCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("deny_code"); - - b.Property("Disposition") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("disposition"); - - b.Property("ExpiresInSeconds") - .HasColumnType("integer") - .HasColumnName("expires_in_seconds"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("SignatureMode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("signature_mode"); - - b.Property("StorageProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("storage_provider"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("Visibility") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_asset_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_content_asset_access_events_user_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_asset_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_user_id_created_at") - .HasFilter("user_id is not null"); - - b.HasIndex("TenantId", "AccessType", "Result", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_access_type_result_cr~"); - - b.ToTable("content_asset_access_events", null, t => - { - t.HasCheckConstraint("ck_content_asset_access_events_expiry", "expires_in_seconds is null or expires_in_seconds > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.PrimitiveCollection("IssueCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("issue_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RiskLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("risk_level"); - - b.Property("ScanStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scan_status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_content_asset_security_scan_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_security_scan_events_tenant_id_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_asset_id_creat~"); - - b.HasIndex("TenantId", "ScanStatus", "RiskLevel", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_scan_status_ri~"); - - b.ToTable("content_asset_security_scan_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("description"); - - b.Property("EntryKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("entry_key"); - - b.Property("EntryType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entry_type"); - - b.Property("Icon") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LayoutConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("layout_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_entries_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_entries_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_entries_updated_by"); - - b.HasIndex("TenantId", "EntryKey") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_entry_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "EntryType", "IsActive", "SortOrder") - .HasDatabaseName("ix_content_entries_tenant_id_region_id_entry_type_is_active_so~"); - - b.ToTable("content_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("Message") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("message"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "ItemId") - .HasDatabaseName("ix_content_import_issues_tenant_id_item_id"); - - b.HasIndex("TenantId", "JobId", "Severity", "RowNo") - .HasDatabaseName("ix_content_import_issues_tenant_id_job_id_severity_row_no"); - - b.ToTable("content_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("content_hash"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExternalId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("external_id"); - - b.Property("IssuesCount") - .HasColumnType("integer") - .HasColumnName("issues_count"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("SourcePayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_items_tenant_id_id"); - - b.HasIndex("JobId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_content_import_items_job_id_row_no"); - - b.HasIndex("TenantId", "JobId", "Status", "RowNo") - .HasDatabaseName("ix_content_import_items_tenant_id_job_id_status_row_no"); - - b.ToTable("content_import_items", null, t => - { - t.HasCheckConstraint("ck_content_import_items_issues_count", "issues_count >= 0"); - - t.HasCheckConstraint("ck_content_import_items_row_no", "row_no >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DryRun") - .HasColumnType("boolean") - .HasColumnName("dry_run"); - - b.Property("ErrorCount") - .HasColumnType("integer") - .HasColumnName("error_count"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("ImportType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("import_type"); - - b.Property("InsertedCount") - .HasColumnType("integer") - .HasColumnName("inserted_count"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SkippedCount") - .HasColumnType("integer") - .HasColumnName("skipped_count"); - - b.Property("SourceFormat") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_format"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TargetCategoryId") - .HasColumnType("uuid") - .HasColumnName("target_category_id"); - - b.Property("TargetContentNodeId") - .HasColumnType("uuid") - .HasColumnName("target_content_node_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TargetRegionId") - .HasColumnType("uuid") - .HasColumnName("target_region_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedCount") - .HasColumnType("integer") - .HasColumnName("updated_count"); - - b.Property("ValidCount") - .HasColumnType("integer") - .HasColumnName("valid_count"); - - b.Property("WarningCount") - .HasColumnType("integer") - .HasColumnName("warning_count"); - - b.HasKey("Id") - .HasName("pk_content_import_jobs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_jobs_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_import_jobs_created_by"); - - b.HasIndex("TenantId", "TargetCategoryId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_category_id"); - - b.HasIndex("TenantId", "TargetContentNodeId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_content_node_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_question_bank_id"); - - b.HasIndex("TenantId", "TargetRegionId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_region_id"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_subject_id"); - - b.HasIndex("TenantId", "ImportType", "Status", "CreatedAt") - .HasDatabaseName("ix_content_import_jobs_tenant_id_import_type_status_created_at"); - - b.ToTable("content_import_jobs", null, t => - { - t.HasCheckConstraint("ck_content_import_jobs_counts", "total_count >= 0 and valid_count >= 0 and error_count >= 0 and warning_count >= 0 and inserted_count >= 0 and updated_count >= 0 and skipped_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Depth") - .HasColumnType("integer") - .HasColumnName("depth"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsLeaf") - .HasColumnType("boolean") - .HasColumnName("is_leaf"); - - b.Property("IsSelectable") - .HasColumnType("boolean") - .HasColumnName("is_selectable"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MarkerConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("marker_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MarkerType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("marker_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("node_key"); - - b.Property("NodeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("node_type"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasColumnType("ltree") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_content_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_nodes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_nodes_created_by"); - - b.HasIndex("Path") - .HasDatabaseName("ix_content_nodes_path"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Path"), "gist"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_nodes_updated_by"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MarkerType") - .HasDatabaseName("ix_content_nodes_tenant_id_marker_type") - .HasFilter("marker_type is not null"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_content_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_nodes_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "NodeKey") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_node_key"); - - b.HasIndex("TenantId", "EntryId", "ParentId", "SortOrder") - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_parent_id_sort_order"); - - b.ToTable("content_nodes", null, t => - { - t.HasCheckConstraint("ck_content_nodes_depth", "depth >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_chapters"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_chapters_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_chapters_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_subject_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_chapters_tenant_id_entry_id_content_node_id_subjec~"); - - b.ToTable("handbook_chapters", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChapterId") - .HasColumnType("uuid") - .HasColumnName("chapter_id"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_entries_tenant_id_id"); - - b.HasIndex("TenantId", "ChapterId") - .HasDatabaseName("ix_handbook_entries_tenant_id_chapter_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_entries_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "ChapterId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_entries_tenant_id_entry_id_content_node_id_chapter~"); - - b.ToTable("handbook_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_school_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_subjects_tenant_id_entry_id_content_node_id_region~"); - - b.ToTable("handbook_subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_images"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_images_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_images_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsPublic") - .HasDatabaseName("ix_images_tenant_id_category_is_public"); - - b.ToTable("images", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssemblyType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assembly_type"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PassScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("pass_score"); - - b.Property("QuestionLimit") - .HasColumnType("integer") - .HasColumnName("question_limit"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Rules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Sections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sections") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_practice_blueprints"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_blueprints_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_practice_blueprints_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_practice_blueprints_updated_by"); - - b.HasIndex("TenantId", "CollectionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_collection_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_practice_blueprints_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_region_id"); - - b.HasIndex("TenantId", "NodeId", "CollectionId", "Mode", "Status", "SortOrder") - .HasDatabaseName("ix_practice_blueprints_tenant_id_node_id_collection_id_mode_st~"); - - b.ToTable("practice_blueprints", null, t => - { - t.HasCheckConstraint("ck_practice_blueprints_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_practice_blueprints_question_limit", "question_limit is null or question_limit > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("AllowedPlanCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("allowed_plan_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.PrimitiveCollection("AllowedRegionIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_region_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedSubjectIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_subject_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedTenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("GrantScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("grant_scope"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_bank_grants"); - - b.HasIndex("AllowedPlanCodes") - .HasDatabaseName("ix_question_bank_grants_allowed_plan_codes"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedPlanCodes"), "gin"); - - b.HasIndex("AllowedTenantIds") - .HasDatabaseName("ix_question_bank_grants_allowed_tenant_ids"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedTenantIds"), "gin"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_bank_grants_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_bank_grants_updated_by"); - - b.HasIndex("SourceQuestionBankId", "Status", "StartsAt", "ExpiresAt") - .HasDatabaseName("ix_question_bank_grants_source_question_bank_id_status_starts_~"); - - b.ToTable("question_bank_grants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CollectionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("collection_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Filters") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("filters") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_collections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collections_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_collections_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_collections_updated_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_question_collections_tenant_id_category_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_question_collections_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_collections_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_question_collections_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_collections_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_question_collections_tenant_id_subject_id"); - - b.HasIndex("TenantId", "NodeId", "Status", "SortOrder") - .HasDatabaseName("ix_question_collections_tenant_id_node_id_status_sort_order"); - - b.ToTable("question_collections", null, t => - { - t.HasCheckConstraint("ck_question_collections_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_question_collections_question_count", "question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Required") - .HasColumnType("boolean") - .HasColumnName("required"); - - b.Property("Score") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_collection_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collection_items_tenant_id_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_question_collection_items_tenant_id_question_id"); - - b.HasIndex("TenantId", "CollectionId", "QuestionId") - .IsUnique() - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_question_~"); - - b.HasIndex("TenantId", "CollectionId", "SectionKey", "SortOrder") - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_section_k~"); - - b.ToTable("question_collection_items", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Types") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("types") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_type_groups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_type_groups_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_type_groups_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_question_type_groups_tenant_id_subject_id_is_active_sort_or~"); - - b.ToTable("question_type_groups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("LegacyVideoId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_video_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoId") - .HasColumnType("uuid") - .HasColumnName("video_id"); - - b.Property("VideoType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("video_type"); - - b.HasKey("Id") - .HasName("pk_question_videos"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_videos_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "VideoId") - .HasDatabaseName("ix_question_videos_tenant_id_video_id"); - - b.HasIndex("TenantId", "QuestionId", "VideoId", "VideoType") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_question_id_video_id_video_type"); - - b.ToTable("question_videos", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("SourceSubjectId") - .HasColumnType("uuid") - .HasColumnName("source_subject_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacySourceSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_source_subject_id"); - - b.Property("LegacyTargetSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_target_subject_id"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.HasKey("TenantId", "SourceSubjectId", "TargetSubjectId") - .HasName("pk_subject_shares"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_subject_shares_tenant_id_target_subject_id"); - - b.ToTable("subject_shares", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AdoptionMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("adoption_mode"); - - b.Property("CopiedQuestionCount") - .HasColumnType("integer") - .HasColumnName("copied_question_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("GrantId") - .HasColumnType("uuid") - .HasColumnName("grant_id"); - - b.Property("LastSyncedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_synced_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("SourceSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SyncStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("sync_status"); - - b.Property("TargetCollectionId") - .HasColumnType("uuid") - .HasColumnName("target_collection_id"); - - b.Property("TargetEntryId") - .HasColumnType("uuid") - .HasColumnName("target_entry_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_question_bank_adoptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_question_bank_adoptions_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_created_by"); - - b.HasIndex("GrantId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_grant_id"); - - b.HasIndex("SourceQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_source_question_bank_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_updated_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .IsUnique() - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_source_question_ba~"); - - b.HasIndex("TenantId", "TargetCollectionId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_collection_~"); - - b.HasIndex("TenantId", "TargetEntryId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_entry_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_question_ba~"); - - b.HasIndex("TenantId", "Status", "UpdatedAt") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_status_updated_at"); - - b.ToTable("tenant_question_bank_adoptions", null, t => - { - t.HasCheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FavoritedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("favorited_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Note") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.HasKey("Id") - .HasName("pk_user_word_favorites"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_favorites_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_favorites_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_favorites_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_favorites_tenant_id_user_id_word_id"); - - b.ToTable("user_word_favorites", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CorrectStreak") - .HasColumnType("integer") - .HasColumnName("correct_streak"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DueLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("due_level"); - - b.Property("EaseFactor") - .ValueGeneratedOnAdd() - .HasPrecision(4, 2) - .HasColumnType("numeric(4,2)") - .HasDefaultValue(2.50m) - .HasColumnName("ease_factor"); - - b.Property("LastResult") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("last_result"); - - b.Property("LastReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_review_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_review_at"); - - b.Property("ReviewCount") - .HasColumnType("integer") - .HasColumnName("review_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_user_word_progress"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_progress_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_progress_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_progress_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_word_id"); - - b.HasIndex("TenantId", "UserId", "NextReviewAt", "Status", "DueLevel") - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_next_review_at_status_~"); - - b.ToTable("user_word_progress", null, t => - { - t.HasCheckConstraint("ck_user_word_progress_correct_streak", "correct_streak >= 0"); - - t.HasCheckConstraint("ck_user_word_progress_ease_factor", "ease_factor >= 1.30 and ease_factor <= 3.00"); - - t.HasCheckConstraint("ck_user_word_progress_review_count", "review_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsGeneral") - .HasColumnType("boolean") - .HasColumnName("is_general"); - - b.Property("KnowledgeTags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("knowledge_tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ThumbnailUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("thumbnail_url"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("video_url"); - - b.HasKey("Id") - .HasName("pk_video_explanations"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_video_explanations_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_video_explanations_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SourceHash") - .HasDatabaseName("ix_video_explanations_tenant_id_source_hash") - .HasFilter("source_hash is not null"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_video_explanations_tenant_id_subject_id_is_active_sort_order"); - - b.ToTable("video_explanations", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WordCount") - .HasColumnType("integer") - .HasColumnName("word_count"); - - b.HasKey("Id") - .HasName("pk_vocabulary_units"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_units_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_units_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_units_tenant_id_entry_id_content_node_id_region_~"); - - b.ToTable("vocabulary_units", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Example") - .HasColumnType("text") - .HasColumnName("example"); - - b.Property("ExampleTranslation") - .HasColumnType("text") - .HasColumnName("example_translation"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Meaning") - .HasColumnType("text") - .HasColumnName("meaning"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phonetic") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("phonetic"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitId") - .HasColumnType("uuid") - .HasColumnName("unit_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Word") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("word"); - - b.HasKey("Id") - .HasName("pk_vocabulary_words"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_words_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_words_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_words_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UnitId", "IsActive", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_vocabulary_words_tenant_id_unit_id_is_active_sort_order_cre~"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "UnitId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_words_tenant_id_entry_id_content_node_id_unit_id~"); - - b.ToTable("vocabulary_words", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("EffectiveRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("effective_rate"); - - b.Property("GeneratedBy") - .HasColumnType("uuid") - .HasColumnName("generated_by"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidBy") - .HasColumnType("uuid") - .HasColumnName("paid_by"); - - b.Property("PaidUserCount") - .HasColumnType("integer") - .HasColumnName("paid_user_count"); - - b.Property("PaymentAccount") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("payment_account"); - - b.Property("PaymentMethod") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_method"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("SettlementNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("settlement_no"); - - b.Property("SourceCount") - .HasColumnType("integer") - .HasColumnName("source_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlements_tenant_id_id"); - - b.HasIndex("GeneratedBy") - .HasDatabaseName("ix_commission_settlements_generated_by"); - - b.HasIndex("PaidBy") - .HasDatabaseName("ix_commission_settlements_paid_by"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlements_referrer_user_id"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commission_settlements_reviewed_by"); - - b.HasIndex("TenantId", "SettlementNo") - .IsUnique() - .HasDatabaseName("ix_commission_settlements_tenant_id_settlement_no"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart") - .HasDatabaseName("ix_commission_settlements_referrer_start"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart", "PeriodEnd") - .IsUnique() - .HasDatabaseName("ux_commission_settlements_referrer_period"); - - b.ToTable("commission_settlements", null, t => - { - t.HasCheckConstraint("ck_commission_settlements_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_counts", "source_count >= 0 and paid_user_count >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_period", "period_end >= period_start"); - - t.HasCheckConstraint("ck_commission_settlements_rates", "default_rate >= 0 and default_rate <= 1 and (effective_rate is null or (effective_rate >= 0 and effective_rate <= 1))"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AttributionType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("attribution_type"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RateSource") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("rate_source"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("SettlementId") - .HasColumnType("uuid") - .HasColumnName("settlement_id"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_no"); - - b.Property("SourcePaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("source_paid_at"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlement_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlement_items_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlement_items_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_commission_settlement_items_student_user_id"); - - b.HasIndex("TenantId", "SettlementId") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_settlement_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "SourcePaidAt") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_referrer_user_id_sour~"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .IsUnique() - .HasDatabaseName("ix_commission_settlement_items_tenant_id_source_type_source_id"); - - b.ToTable("commission_settlement_items", null, t => - { - t.HasCheckConstraint("ck_commission_settlement_items_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlement_items_rate", "commission_rate >= 0 and commission_rate <= 1"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignmentConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssignmentCursor") - .HasColumnType("integer") - .HasColumnName("assignment_cursor"); - - b.Property("AssignmentMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("AssignmentPool") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_pool") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DelaySeconds") - .HasColumnType("integer") - .HasColumnName("delay_seconds"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("ExamType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("exam_type"); - - b.Property("FormName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("form_name"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Url") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("url"); - - b.HasKey("Id") - .HasName("pk_crm_config"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_config_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_crm_config_tenant_id"); - - b.HasIndex("TenantId", "AssignmentMode") - .HasDatabaseName("ix_crm_config_tenant_id_assignment_mode"); - - b.ToTable("crm_config", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempt") - .HasColumnType("integer") - .HasColumnName("attempt"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("HttpCode") - .HasColumnType("integer") - .HasColumnName("http_code"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Outcome") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("outcome"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("RequestBody") - .HasColumnType("text") - .HasColumnName("request_body"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ResponseSummary") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("response_summary"); - - b.Property("SignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("signed_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_log"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_log_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_log_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_log_tenant_id_record_id_created_at"); - - b.ToTable("crm_webhook_log", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("ScheduledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Source") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("target_url"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_queue"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_queue_tenant_id_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_idempotency_key") - .HasFilter("idempotency_key is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id_created_at") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "Source", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_source_created_at") - .HasFilter("source = 'tenant.student.crm_push'"); - - b.HasIndex("TenantId", "Status", "ScheduledAt", "NextAttemptAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_status_scheduled_at_next_attemp~"); - - b.ToTable("crm_webhook_queue", null, t => - { - t.HasCheckConstraint("ck_crm_webhook_queue_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LandingPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("landing_path"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_codes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_codes_user_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_code"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_user_id"); - - b.HasIndex("TenantId", "Code", "Status") - .HasDatabaseName("ix_referral_codes_tenant_id_code_status"); - - b.ToTable("referral_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("assigned_at"); - - b.Property("AssignedBy") - .HasColumnType("uuid") - .HasColumnName("assigned_by"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("AssignmentMode") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("BindType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bind_type"); - - b.Property("BoundAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("bound_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FirstTrackId") - .HasColumnType("uuid") - .HasColumnName("first_track_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ProtectedUntil") - .HasColumnType("timestamp with time zone") - .HasColumnName("protected_until"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_leads"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_leads_tenant_id_id"); - - b.HasIndex("AssignedBy") - .HasDatabaseName("ix_referral_leads_assigned_by"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_referral_leads_assigned_to_user_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_leads_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_referral_leads_student_user_id"); - - b.HasIndex("TenantId", "FirstTrackId") - .HasDatabaseName("ix_referral_leads_tenant_id_first_track_id"); - - b.HasIndex("TenantId", "StudentUserId") - .IsUnique() - .HasDatabaseName("ix_referral_leads_tenant_id_student_user_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_referrer_user_id_bound_at"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_assigned_to_user_id_status_bound_at"); - - b.ToTable("referral_leads", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Page") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("page"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("QrcodeUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("qrcode_url"); - - b.Property("RefCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("Scene") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("scene"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_qrcodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_qrcodes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_qrcodes_user_id"); - - b.HasIndex("TenantId", "RefCode", "Status") - .HasDatabaseName("ix_referral_qrcodes_tenant_id_ref_code_status"); - - b.HasIndex("TenantId", "Provider", "Scene", "Page") - .IsUnique() - .HasDatabaseName("ix_referral_qrcodes_tenant_id_provider_scene_page"); - - b.ToTable("referral_qrcodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LeaderUserId") - .HasColumnType("uuid") - .HasColumnName("leader_user_id"); - - b.Property("MemberUserId") - .HasColumnType("uuid") - .HasColumnName("member_user_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RelationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("relation_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_team_edges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_team_edges_tenant_id_id"); - - b.HasIndex("LeaderUserId") - .HasDatabaseName("ix_referral_team_edges_leader_user_id"); - - b.HasIndex("MemberUserId") - .HasDatabaseName("ix_referral_team_edges_member_user_id"); - - b.HasIndex("TenantId", "LeaderUserId", "Status") - .HasDatabaseName("ix_referral_team_edges_tenant_id_leader_user_id_status"); - - b.HasIndex("TenantId", "MemberUserId", "RelationType") - .IsUnique() - .HasDatabaseName("ix_referral_team_edges_tenant_id_member_user_id_relation_type"); - - b.ToTable("referral_team_edges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("LeadId") - .HasColumnType("uuid") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("TargetUserId") - .HasColumnType("uuid") - .HasColumnName("target_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_referral_tracks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_tracks_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_tracks_referrer_user_id"); - - b.HasIndex("TargetUserId") - .HasDatabaseName("ix_referral_tracks_target_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_referral_tracks_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "LeadId", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_lead_id_created_at"); - - b.HasIndex("TenantId", "RefCode", "EventType", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_ref_code_event_type_created_at"); - - b.ToTable("referral_tracks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("MinSettlementCents") - .HasColumnType("integer") - .HasColumnName("min_settlement_cents"); - - b.Property("SettlementCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("settlement_cycle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_commission_settings"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_commission_settings_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_commission_settings_tenant_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_commission_settings_updated_by"); - - b.ToTable("tenant_commission_settings", null, t => - { - t.HasCheckConstraint("ck_tenant_commission_settings_default_rate", "default_rate >= 0 and default_rate <= 1"); - - t.HasCheckConstraint("ck_tenant_commission_settings_min_settlement", "min_settlement_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastCheckInDate") - .HasColumnType("date") - .HasColumnName("last_check_in_date"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("MasteredWordsCount") - .HasColumnType("integer") - .HasColumnName("mastered_words_count"); - - b.Property("ModuleSelections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_selections") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Progress") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("progress") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionsAnsweredToday") - .HasColumnType("integer") - .HasColumnName("questions_answered_today"); - - b.Property("RecentActivities") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("recent_activities") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SelectedMajorId") - .HasColumnType("uuid") - .HasColumnName("selected_major_id"); - - b.Property("SelectedSchoolId") - .HasColumnType("uuid") - .HasColumnName("selected_school_id"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_student_profiles"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_student_profiles_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_student_profiles_user_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_student_profiles_tenant_id_region_id"); - - b.HasIndex("TenantId", "SelectedMajorId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_major_id"); - - b.HasIndex("TenantId", "SelectedSchoolId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_school_id"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_student_profiles_tenant_id_user_id"); - - b.ToTable("student_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AvatarUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("avatar_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("LastSeenAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPasswordHash") - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasColumnName("legacy_password_hash"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PasswordMigrationRequired") - .HasColumnType("boolean") - .HasColumnName("password_migration_required"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("PrimaryRole") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("primary_role"); - - b.Property("RawProfile") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_profile") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Score") - .HasColumnType("integer") - .HasColumnName("score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Username") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Email") - .IsUnique() - .HasDatabaseName("ix_users_email"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_users_legacy_id"); - - b.HasIndex("Phone") - .IsUnique() - .HasDatabaseName("ix_users_phone"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("OpenId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("open_id"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderSubject") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("provider_subject"); - - b.Property("SecretPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("secret_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UnionId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("union_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_identities"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_identities_user_id"); - - b.HasIndex("Provider", "ProviderSubject") - .IsUnique() - .HasDatabaseName("ix_user_identities_provider_provider_subject"); - - b.ToTable("user_identities", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("IssueCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("RawValueSample") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("raw_value_sample"); - - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_run_id"); - - b.HasIndex("RunId", "Severity", "CollectionName") - .HasDatabaseName("ix_pb_import_issues_run_id_severity_collection_name"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("SourceKind") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_kind"); - - b.Property("SourceName") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_runs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_runs_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "StartedAt") - .HasDatabaseName("ix_pb_import_runs_tenant_id_status_started_at"); - - b.ToTable("pb_import_runs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("CollectionName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Errors") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("errors") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ImportedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("imported_at") - .HasDefaultValueSql("now()"); - - b.Property("Normalized") - .HasColumnType("boolean") - .HasColumnName("normalized"); - - b.Property("Record") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("record") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("RunId", "CollectionName", "LegacyId") - .HasName("pk_pb_raw_records"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_run_id"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_raw_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("AnsweredAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("answered_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsCorrect") - .HasColumnType("boolean") - .HasColumnName("is_correct"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("QuestionVersionId") - .HasColumnType("uuid") - .HasColumnName("question_version_id"); - - b.Property("SelectedOptions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("selected_options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_answer_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_answer_records_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_answer_records_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_answer_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id"); - - b.HasIndex("TenantId", "QuestionId", "QuestionVersionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id_question_version_id"); - - b.HasIndex("TenantId", "UserId", "PracticeSessionId") - .HasDatabaseName("ix_answer_records_tenant_id_user_id_practice_session_id"); - - b.ToTable("answer_records", null, t => - { - t.HasCheckConstraint("ck_answer_records_version_requires_question", "question_version_id is null or question_id is not null"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActiveUsers") - .HasColumnType("integer") - .HasColumnName("active_users"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("NewOrders") - .HasColumnType("integer") - .HasColumnName("new_orders"); - - b.Property("NewQuestions") - .HasColumnType("integer") - .HasColumnName("new_questions"); - - b.Property("NewRevenueCents") - .HasColumnType("integer") - .HasColumnName("new_revenue_cents"); - - b.Property("NewUsers") - .HasColumnType("integer") - .HasColumnName("new_users"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_dashboard_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_dashboard_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_stat_date_region_id"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId"), false); - - b.ToTable("dashboard_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("ExamAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("exam_at"); - - b.Property("ExamName") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("exam_name"); - - b.Property("ExamType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("exam_type"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_exam_dates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_exam_dates_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_exam_dates_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_exam_dates_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId", "ExamAt") - .HasDatabaseName("ix_exam_dates_tenant_id_school_id_exam_at"); - - b.ToTable("exam_dates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_favorite_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_favorite_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_favorite_questions_tenant_id_question_id"); - - b.ToTable("favorite_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_mode"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementId") - .HasColumnType("uuid") - .HasColumnName("entitlement_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("GrantedCount") - .HasColumnType("integer") - .HasColumnName("granted_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("Reason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("reason"); - - b.Property("RequestedCount") - .HasColumnType("integer") - .HasColumnName("requested_count"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_access_events_user_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_access_events_tenant_id_practice_session_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_practice_access_events_tenant_id_user_id_created_at"); - - b.ToTable("practice_access_events", null, t => - { - t.HasCheckConstraint("ck_practice_access_events_consumed_free_quota", "consumed_free_quota >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_granted_count", "granted_count >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_requested_count", "requested_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FreeLimit") - .HasColumnType("integer") - .HasColumnName("free_limit"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsageDate") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasColumnName("usage_date") - .HasDefaultValueSql("current_date"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_daily_usage"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_daily_usage_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_daily_usage_user_id"); - - b.HasIndex("TenantId", "UserId", "UsageDate") - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date"); - - b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId") - .IsUnique() - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date_scope_typ~"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId"), false); - - b.ToTable("practice_daily_usage", null, t => - { - t.HasCheckConstraint("ck_practice_daily_usage_free_limit", "free_limit >= 0"); - - t.HasCheckConstraint("ck_practice_daily_usage_used_count", "used_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessEntitlementId") - .HasColumnType("uuid") - .HasColumnName("access_entitlement_id"); - - b.Property("AccessMode") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("free") - .HasColumnName("access_mode"); - - b.Property("AccessSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_sessions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "UserId", "Id") - .HasName("ak_practice_sessions_tenant_id_user_id_id"); - - 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"); - - b.ToTable("practice_sessions", null, t => - { - t.HasCheckConstraint("ck_practice_sessions_consumed_free_quota", "consumed_free_quota >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionResults") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_results") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionStats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("section_stats") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("SubmittedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("submitted_at") - .HasDefaultValueSql("now()"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalQuestions") - .HasColumnType("integer") - .HasColumnName("total_questions"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.Property("WrongQuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("wrong_question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.HasKey("Id") - .HasName("pk_practice_session_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_reports_tenant_id_id"); - - 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.ToTable("practice_session_reports", null, t => - { - t.HasCheckConstraint("ck_practice_session_reports_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_reports_counts", "total_questions >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_duration", "duration_seconds >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("question_type"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SectionName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("section_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_practice_session_report_sections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_report_sections_tenant_id_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_practice_session~"); - - b.HasIndex("TenantId", "ReportId", "SectionKey") - .IsUnique() - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sectio~"); - - b.HasIndex("TenantId", "ReportId", "SortOrder") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sort_o~"); - - b.ToTable("practice_session_report_sections", null, t => - { - t.HasCheckConstraint("ck_practice_session_report_sections_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_report_sections_counts", "question_count >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_report_sections_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastAccessAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_access_at"); - - b.Property("LastPracticeAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_practice_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("practice_type"); - - b.Property("Progress") - .HasColumnType("integer") - .HasColumnName("progress"); - - b.Property("TargetLegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("target_legacy_id"); - - b.Property("TargetName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("target_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_recent_practices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_recent_practices_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_recent_practices_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_recent_practices_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "LastAccessAt") - .HasDatabaseName("ix_recent_practices_tenant_id_user_id_last_access_at"); - - b.ToTable("recent_practices", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attachments") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("attachments") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("Contact") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("contact"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("HandledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("handled_at"); - - b.Property("HandledBy") - .HasColumnType("uuid") - .HasColumnName("handled_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Resolution") - .HasColumnType("text") - .HasColumnName("resolution"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_reports_tenant_id_id"); - - b.HasIndex("HandledBy") - .HasDatabaseName("ix_reports_handled_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_reports_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_reports_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_question_id_created_at") - .HasFilter("question_id is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_status_created_at"); - - b.ToTable("reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_report_status_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_report_status_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_report_status_events_actor_user_id"); - - b.HasIndex("TenantId", "ReportId", "CreatedAt") - .HasDatabaseName("ix_report_status_events_tenant_id_report_id_created_at"); - - b.ToTable("report_status_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CodeCount") - .HasColumnType("integer") - .HasColumnName("code_count"); - - b.Property("CodeEstimated") - .HasColumnType("integer") - .HasColumnName("code_estimated"); - - b.Property("CodeUsed") - .HasColumnType("integer") - .HasColumnName("code_used"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EstimatedRevenueCents") - .HasColumnType("integer") - .HasColumnName("estimated_revenue_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("OrderCount") - .HasColumnType("integer") - .HasColumnName("order_count"); - - b.Property("RealRevenueCents") - .HasColumnType("integer") - .HasColumnName("real_revenue_cents"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_revenue_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_revenue_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_stat_date_region_id_sale_type"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType"), false); - - b.ToTable("revenue_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceAfter") - .HasColumnType("integer") - .HasColumnName("balance_after"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Points") - .HasColumnType("integer") - .HasColumnName("points"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_score_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_score_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_score_events_user_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_user_score_events_tenant_id_idempotency_key"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_user_score_events_tenant_id_user_id_created_at"); - - b.ToTable("user_score_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("LastWrongAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_wrong_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("WrongCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(1) - .HasColumnName("wrong_count"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_wrong_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_wrong_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_wrong_questions_tenant_id_question_id"); - - b.ToTable("wrong_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_announcements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_announcements_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_announcements_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "IsActive", "SortOrder") - .HasDatabaseName("ix_announcements_tenant_id_is_active_sort_order"); - - b.ToTable("announcements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_audit_logs"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_audit_logs_actor_user_id"); - - b.HasIndex("TenantId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_created_at"); - - b.HasIndex("TenantId", "TargetType", "TargetId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_target_type_target_id_created_at"); - - b.ToTable("audit_logs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("ConditionExtra") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("condition_extra") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ConditionField") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("condition_field"); - - b.Property("ConditionOperator") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("condition_operator"); - - b.Property("ConditionValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("condition_value"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon_url"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Level") - .HasColumnType("integer") - .HasColumnName("level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnlockType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("unlock_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_badges_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsActive", "SortOrder") - .HasDatabaseName("ix_badges_tenant_id_category_is_active_sort_order"); - - b.ToTable("badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("BorderColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("border_color"); - - b.Property("ButtonLink") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("button_link"); - - b.Property("ButtonText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("button_text"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Subtitle") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("subtitle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_banners"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_banners_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_banners_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_banners_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("banners", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Answer") - .HasColumnType("text") - .HasColumnName("answer"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Question") - .HasColumnType("text") - .HasColumnName("question"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_faqs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_faqs_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_faqs_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_faqs_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("faqs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("AdoptionId") - .HasColumnType("uuid") - .HasColumnName("adoption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("ReadBy") - .HasColumnType("uuid") - .HasColumnName("read_by"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_content_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_content_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_content_notifications_created_by"); - - b.HasIndex("ReadBy") - .HasDatabaseName("ix_tenant_content_notifications_read_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_source_question_bank~"); - - b.HasIndex("TenantId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_notification_type_de~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "AdoptionId", "NotificationType", "Status") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_adoption_id_notifica~"); - - b.ToTable("tenant_content_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActivePublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ActiveTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("active_template_code"); - - b.Property("ActiveTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DraftPublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("draft_template_code"); - - b.Property("DraftTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftUpdatedBy") - .HasColumnType("uuid") - .HasColumnName("draft_updated_by"); - - b.Property("PublishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("published_at"); - - b.Property("PublishedBy") - .HasColumnType("uuid") - .HasColumnName("published_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_configs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_theme_configs_tenant_id_id"); - - b.HasIndex("ActiveTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_active_template_code"); - - b.HasIndex("DraftTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_draft_template_code"); - - b.HasIndex("DraftUpdatedBy") - .HasDatabaseName("ix_tenant_theme_configs_draft_updated_by"); - - b.HasIndex("PublishedBy") - .HasDatabaseName("ix_tenant_theme_configs_published_by"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_configs_tenant_id"); - - b.ToTable("tenant_theme_configs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PreviewImageUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_image_url"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_templates"); - - b.HasAlternateKey("Code") - .HasName("ak_tenant_theme_templates_code"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_templates_code"); - - b.HasIndex("Status", "SortOrder", "Code") - .HasDatabaseName("ix_tenant_theme_templates_status_sort_order_code"); - - b.ToTable("tenant_theme_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BadgeId") - .HasColumnType("uuid") - .HasColumnName("badge_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrantedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("granted_at"); - - b.Property("GrantedBy") - .HasColumnType("uuid") - .HasColumnName("granted_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_badges_tenant_id_id"); - - b.HasIndex("GrantedBy") - .HasDatabaseName("ix_user_badges_granted_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_badges_user_id"); - - b.HasIndex("TenantId", "BadgeId") - .HasDatabaseName("ix_user_badges_tenant_id_badge_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "BadgeId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_user_id_badge_id"); - - b.ToTable("user_badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_user_notifications_created_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_notifications_user_id"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .HasDatabaseName("ix_user_notifications_tenant_id_source_type_source_id"); - - b.HasIndex("TenantId", "UserId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_notification_type_dedu~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "UserId", "Status", "CreatedAt") - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_status_created_at"); - - b.ToTable("user_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("AcknowledgedBy") - .HasColumnType("uuid") - .HasColumnName("acknowledged_by"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("AuditLogId") - .HasColumnType("uuid") - .HasColumnName("audit_log_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FirstSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("first_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("LastSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("RuleId") - .HasColumnType("uuid") - .HasColumnName("rule_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alerts"); - - b.HasIndex("AcknowledgedBy") - .HasDatabaseName("ix_platform_audit_alerts_acknowledged_by"); - - b.HasIndex("AuditLogId") - .HasDatabaseName("ix_platform_audit_alerts_audit_log_id"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_platform_audit_alerts_resolved_by"); - - b.HasIndex("RuleId", "AuditLogId") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alerts_rule_id_audit_log_id"); - - b.HasIndex("Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_status_severity_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_tenant_id_status_created_at"); - - b.ToTable("platform_audit_alerts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("ActionPatterns") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("action_patterns") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("Conditions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("conditions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.PrimitiveCollection("TargetTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("target_types") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alert_rules"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alert_rules_code"); - - b.HasIndex("TenantId") - .HasDatabaseName("ix_platform_audit_alert_rules_tenant_id"); - - b.HasIndex("Enabled", "Severity", "Code") - .HasDatabaseName("ix_platform_audit_alert_rules_enabled_severity_code"); - - b.ToTable("platform_audit_alert_rules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationChannel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChannelCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel_code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MinReminderLevel") - .HasColumnType("integer") - .HasColumnName("min_reminder_level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.PrimitiveCollection("ReminderChannels") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_channels") - .HasDefaultValueSql("array['internal']::text[]"); - - b.PrimitiveCollection("ReminderTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_types") - .HasDefaultValueSql("array['overdue', 'final_notice']::text[]"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.PrimitiveCollection("TenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WebhookUrl") - .IsRequired() - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("webhook_url"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_channels"); - - b.HasIndex("ChannelCode") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_channels_channel_code"); - - b.HasIndex("Enabled", "MinReminderLevel", "ChannelCode") - .HasDatabaseName("ix_platform_dunning_notification_channels_enabled_min_reminder~"); - - b.ToTable("platform_dunning_notification_channels", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_channels_level", "min_reminder_level between 1 and 20"); - - t.HasCheckConstraint("ck_platform_dunning_channels_timeout", "timeout_seconds between 1 and 60"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("ChannelId") - .HasColumnType("uuid") - .HasColumnName("channel_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.Property("ReminderId") - .HasColumnType("uuid") - .HasColumnName("reminder_id"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScheduledAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at") - .HasDefaultValueSql("now()"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_platform_dunning_notification_events_tenant_id_id"); - - b.HasIndex("ChannelId", "ReminderId") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_events_channel_id_reminder_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_invoice_id"); - - b.HasIndex("TenantId", "ReminderId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_reminder_id"); - - b.HasIndex("InvoiceId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_invoice_id_status_crea~"); - - b.HasIndex("ReminderId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_reminder_id_status_cre~"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_status_creat~"); - - b.HasIndex("Status", "NextAttemptAt", "ScheduledAt", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_status_next_attempt_at~"); - - b.ToTable("platform_dunning_notification_events", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_events_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformSaasPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseAmountCents") - .HasColumnType("integer") - .HasColumnName("base_amount_cents"); - - b.Property("BillingCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_cycle"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IncludedQuotas") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("included_quotas") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OveragePrices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("overage_prices") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_saas_plans"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_saas_plans_code"); - - b.HasIndex("Status", "SortOrder") - .HasDatabaseName("ix_platform_saas_plans_status_sort_order"); - - b.ToTable("platform_saas_plans", null, t => - { - t.HasCheckConstraint("ck_platform_saas_plans_amount", "base_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BankAccountMasked") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("bank_account_masked"); - - b.Property("BankName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bank_name"); - - b.Property("BillingAddress") - .HasColumnType("text") - .HasColumnName("billing_address"); - - b.Property("BillingName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("billing_name"); - - b.Property("ContactEmail") - .HasMaxLength(300) - .HasColumnType("citext") - .HasColumnName("contact_email"); - - b.Property("ContactName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("contact_name"); - - b.Property("ContactPhone") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("contact_phone"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceTitle") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("invoice_title"); - - b.Property("InvoiceType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TaxId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("tax_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_billing_profiles"); - - b.ToTable("tenant_billing_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceCents") - .HasColumnType("integer") - .HasColumnName("balance_cents"); - - b.Property("BillingPeriodEnd") - .HasColumnType("date") - .HasColumnName("billing_period_end"); - - b.Property("BillingPeriodStart") - .HasColumnType("date") - .HasColumnName("billing_period_start"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("DiscountCents") - .HasColumnType("integer") - .HasColumnName("discount_cents"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("invoice_no"); - - b.Property("InvoiceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidCents") - .HasColumnType("integer") - .HasColumnName("paid_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubtotalCents") - .HasColumnType("integer") - .HasColumnName("subtotal_cents"); - - b.Property("TaxCents") - .HasColumnType("integer") - .HasColumnName("tax_cents"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCents") - .HasColumnType("integer") - .HasColumnName("total_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoices_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoices_created_by"); - - b.HasIndex("InvoiceNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_invoice_no"); - - b.HasIndex("TenantId", "BillingPeriodStart", "BillingPeriodEnd") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_tenant_id_billing_period_start_billing_peri~") - .HasFilter("invoice_type = 'usage_overage' and status <> 'void' and metadata->>'source' = 'usage_overage_auto'"); - - b.HasIndex("TenantId", "Status", "DueDate") - .HasDatabaseName("ix_tenant_invoices_tenant_id_status_due_date"); - - b.ToTable("tenant_invoices", null, t => - { - t.HasCheckConstraint("ck_tenant_invoices_amounts", "subtotal_cents >= 0 and discount_cents >= 0 and tax_cents >= 0 and total_cents >= 0 and paid_cents >= 0 and balance_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoices_period", "billing_period_end is null or billing_period_start is null or billing_period_end >= billing_period_start"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("description"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("ItemRefId") - .HasColumnType("uuid") - .HasColumnName("item_ref_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Quantity") - .HasPrecision(12, 2) - .HasColumnType("numeric(12,2)") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_items_tenant_id_id"); - - b.HasIndex("InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_invoice_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_items", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_items_amounts", "unit_amount_cents >= 0 and amount_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_no"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReceivedBy") - .HasColumnType("uuid") - .HasColumnName("received_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_payments_tenant_id_id"); - - b.HasIndex("PaymentNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_payments_payment_no"); - - b.HasIndex("ReceivedBy") - .HasDatabaseName("ix_tenant_invoice_payments_received_by"); - - b.HasIndex("InvoiceId", "Status") - .HasDatabaseName("ix_tenant_invoice_payments_invoice_id_status"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_payments_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_payments", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("BalanceCentsSnapshot") - .HasColumnType("integer") - .HasColumnName("balance_cents_snapshot"); - - b.Property("Channel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("channel"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Message") - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReminderDate") - .HasColumnType("date") - .HasColumnName("reminder_date"); - - b.Property("ReminderLevel") - .HasColumnType("integer") - .HasColumnName("reminder_level"); - - b.Property("ReminderType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("reminder_type"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_reminders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_reminders_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoice_reminders_created_by"); - - b.HasIndex("InvoiceId", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_invoice_id_reminder_date"); - - b.HasIndex("TenantId", "Status", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_status_reminder_date"); - - b.HasIndex("TenantId", "InvoiceId", "ReminderType", "Channel", "ReminderDate") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_invoice_id_reminder_type~"); - - b.ToTable("tenant_invoice_reminders", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_reminders_balance", "balance_cents_snapshot >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_reminders_level", "reminder_level between 1 and 20"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CurrentVersionId") - .HasColumnType("uuid") - .HasColumnName("current_version_id"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExamMarkers") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("exam_markers") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("HasVideoExplanation") - .HasColumnType("boolean") - .HasColumnName("has_video_explanation"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyNodeId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_node_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("MediaUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("media_url"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PrimaryCollectionId") - .HasColumnType("uuid") - .HasColumnName("primary_collection_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("TypeLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("type_label"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_questions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_questions_tenant_id_id"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_questions_tenant_id_category_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_questions_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_questions_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_questions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_questions_tenant_id_node_id"); - - b.HasIndex("TenantId", "PrimaryCollectionId") - .HasDatabaseName("ix_questions_tenant_id_primary_collection_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_questions_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_questions_tenant_id_subject_id"); - - b.HasIndex("TenantId", "Id", "CurrentVersionId") - .HasDatabaseName("ix_questions_tenant_id_id_current_version_id"); - - b.ToTable("questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SourceScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_scope"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_banks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_banks_tenant_id_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_banks_tenant_id_region_id"); - - b.ToTable("question_banks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("CodeLang") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code_lang"); - - b.Property("CodeTemplate") - .HasColumnType("text") - .HasColumnName("code_template"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CorrectOptionIndex") - .HasColumnType("integer") - .HasColumnName("correct_option_index"); - - b.Property("CorrectOptionIndices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("correct_option_indices") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Explanation") - .HasColumnType("text") - .HasColumnName("explanation"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubQuestions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sub_questions") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("VersionNo") - .HasColumnType("integer") - .HasColumnName("version_no"); - - b.HasKey("Id") - .HasName("pk_question_versions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_versions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "QuestionId", "Id") - .HasName("ak_question_versions_tenant_id_question_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_versions_created_by"); - - b.HasIndex("QuestionId", "VersionNo") - .IsUnique() - .HasDatabaseName("ix_question_versions_question_id_version_no"); - - b.ToTable("question_versions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FailureCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("failure_code"); - - b.Property("Identifier") - .HasMaxLength(320) - .HasColumnType("character varying(320)") - .HasColumnName("identifier"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_login_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_login_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_login_events_user_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_auth_login_events_tenant_id_user_id_created_at"); - - b.ToTable("auth_login_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TokenHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("token_hash"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_sessions_tenant_id_id"); - - b.HasIndex("TokenHash") - .IsUnique() - .HasDatabaseName("ix_auth_sessions_token_hash"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_sessions_user_id"); - - b.HasIndex("TenantId", "UserId", "ExpiresAt") - .HasDatabaseName("ix_auth_sessions_tenant_id_user_id_expires_at") - .HasFilter("revoked_at is null"); - - b.ToTable("auth_sessions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Dimension") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("dimension"); - - b.Property("ScopeHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("scope_hash"); - - b.Property("BucketStart") - .HasColumnType("timestamp with time zone") - .HasColumnName("bucket_start"); - - b.Property("RequestCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(0) - .HasColumnName("request_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId", "Dimension", "ScopeHash", "BucketStart") - .HasName("pk_sms_send_rate_limits"); - - b.HasIndex("UpdatedAt") - .HasDatabaseName("ix_sms_send_rate_limits_updated_at"); - - b.ToTable("sms_send_rate_limits", null, t => - { - t.HasCheckConstraint("ck_sms_send_rate_limits_request_count", "request_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CodeHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("code_hash"); - - b.Property("ConsumedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("consumed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phone") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Purpose") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("purpose"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_sms_verification_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_sms_verification_codes_tenant_id_id"); - - b.HasIndex("TenantId", "Phone", "Purpose") - .IsUnique() - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "CreatedAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_created_at") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "ExpiresAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_expires_at"); - - b.ToTable("sms_verification_codes", null, t => - { - t.HasCheckConstraint("ck_sms_verification_codes_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BillingStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_status"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegalName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("legal_name"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OwnerUserId") - .HasColumnType("uuid") - .HasColumnName("owner_user_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("slug"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenants"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenants_legacy_id"); - - b.HasIndex("OwnerUserId") - .HasDatabaseName("ix_tenants_owner_user_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_tenants_slug"); - - b.ToTable("tenants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_auth_providers"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_auth_providers_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider"); - - b.HasIndex("TenantId", "Provider", "Status") - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider_status"); - - b.ToTable("tenant_auth_providers", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BrandName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("brand_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FaviconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("favicon_url"); - - b.Property("LogoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("logo_url"); - - b.Property("OrganizationName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("organization_name"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ServiceAccountName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("service_account_name"); - - b.Property("ServiceWechat") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("service_wechat"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("Slogan") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("slogan"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_branding"); - - b.ToTable("tenant_branding", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_classes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_classes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_classes_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_classes_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_code") - .HasFilter("code is not null and code <> ''"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_tenant_classes_tenant_id_region_id") - .HasFilter("region_id is not null"); - - b.HasIndex("TenantId", "Status", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_tenant_classes_tenant_id_status_sort_order_created_at"); - - b.ToTable("tenant_classes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("JoinedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("joined_at") - .HasDefaultValueSql("now()"); - - b.Property("LeftAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("left_at"); - - b.Property("MemberType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("member_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_class_members"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_class_members_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_class_members_user_id"); - - b.HasIndex("TenantId", "ClassId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_status_member_type"); - - b.HasIndex("TenantId", "ClassId", "UserId", "MemberType") - .IsUnique() - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_user_id_member_type"); - - b.HasIndex("TenantId", "UserId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_user_id_status_member_type"); - - b.ToTable("tenant_class_members", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DomainType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("domain_type"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(253) - .HasColumnType("citext") - .HasColumnName("host"); - - b.Property("IsPrimary") - .HasColumnType("boolean") - .HasColumnName("is_primary"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VerificationToken") - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("verification_token"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.HasKey("Id") - .HasName("pk_tenant_domains"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_domains_tenant_id_id"); - - b.HasIndex("Host") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_host"); - - b.HasIndex("TenantId", "IsPrimary") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_tenant_id_is_primary") - .HasFilter("is_primary"); - - b.ToTable("tenant_domains", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyRole") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("legacy_role"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Role") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("role"); - - b.Property("RoleTemplateId") - .HasColumnType("uuid") - .HasColumnName("role_template_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_memberships"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_memberships_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_memberships_user_id"); - - b.HasIndex("TenantId", "RoleTemplateId") - .HasDatabaseName("ix_tenant_memberships_tenant_id_role_template_id"); - - b.HasIndex("TenantId", "UserId", "Role") - .IsUnique() - .HasDatabaseName("ix_tenant_memberships_tenant_id_user_id_role"); - - b.ToTable("tenant_memberships", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("base_role"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DataScope") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("data_scope") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FieldPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IsSystem") - .HasColumnType("boolean") - .HasColumnName("is_system"); - - b.Property("MenuPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("menu_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModulePermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_role_templates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_role_templates_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_role_templates_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_role_templates_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_role_templates_tenant_id_code"); - - b.HasIndex("TenantId", "Status", "SortOrder") - .HasDatabaseName("ix_tenant_role_templates_tenant_id_status_sort_order"); - - b.ToTable("tenant_role_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("AdminFeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("admin_feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PublicConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_settings"); - - b.ToTable("tenant_settings", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CompletedBy") - .HasColumnType("uuid") - .HasColumnName("completed_by"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("FollowupType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("followup_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_student_followups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_followups_tenant_id_id"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_tenant_student_followups_assigned_to_user_id"); - - b.HasIndex("CompletedBy") - .HasDatabaseName("ix_tenant_student_followups_completed_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_followups_student_user_id"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_assigned_to_user_id_stat~") - .HasFilter("assigned_to_user_id is not null"); - - b.HasIndex("TenantId", "ClassId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_class_id_status_due_at") - .HasFilter("class_id is not null"); - - b.HasIndex("TenantId", "StudentUserId", "Status", "DueAt", "CreatedAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_student_user_id_status_d~"); - - b.ToTable("tenant_student_followups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("IsPinned") - .HasColumnType("boolean") - .HasColumnName("is_pinned"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NoteType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("note_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_tenant_student_notes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_notes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_student_notes_created_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_notes_student_user_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_student_notes_updated_by"); - - b.HasIndex("TenantId", "CreatedBy", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_created_by_created_at") - .HasFilter("created_by is not null"); - - b.HasIndex("TenantId", "StudentUserId", "IsPinned", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_student_user_id_is_pinned_cr~"); - - b.ToTable("tenant_student_notes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_categories_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_majors_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_module_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_module_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_regions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_region_modules_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_region_modules_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_schools_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AgentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_agent_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_activation_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UsedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_used_by"); - - b.HasOne("Tiku.Domain.Commerce.CodeBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_code_batches_tenant_id_batch_id"); - - b.HasOne("Tiku.Domain.Commerce.CouponRedemption", null) - .WithMany() - .HasForeignKey("TenantId", "CouponRedemptionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_coupon_redemptions_tenant_id_coupon_redemp~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "UsedRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_regions_tenant_id_used_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_code_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_batches_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedTo") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_assigned_to"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_resolved_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_batc~"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_item~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_refund_requests_ten~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issue_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationIssue", null) - .WithMany() - .HasForeignKey("TenantId", "IssueId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_commerce_reconciliatio~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_commerce_reconciliation_batch~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_commerce_refund_requests_tena~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_commerce_refund_requests_tenant_id_r~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ProcessedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_processed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RequestedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_requested_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_refund_requests_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupons_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_coupons_svip_plans_tenant_id_plan_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupon_redemptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.Coupon", null) - .WithMany() - .HasForeignKey("TenantId", "CouponId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_coupons_tenant_id_coupon_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RevokedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_entitlements_users_revoked_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_orders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_orders_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payment_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_payment_events_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_products_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_products_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_svip_plans_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_svip_plans_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_payment_accounts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_subscriptions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_usage_records_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_ai_recommendation_reports_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_app_assets_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_assets_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("VerifiedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_verified_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_content_nodes_tenant_id_content_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_asset_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_access_events_content_assets_tenant_id_asset_~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_security_scan_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_security_scan_events_content_assets_tenant_id~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_entries_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_import_issues_content_import_items_tenant_id_item_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_import_jobs_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_jobs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_categories_tenant_id_target_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "TargetContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_content_nodes_tenant_id_target_content_~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_question_banks_tenant_id_target_questio~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "TargetRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_regions_tenant_id_target_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_nodes_content_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_chapters_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_chapters_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_handbook_chapters_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.HandbookSubject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_chapters_handbook_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.HandbookChapter", null) - .WithMany() - .HasForeignKey("TenantId", "ChapterId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_entries_handbook_chapters_tenant_id_chapter_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_entries_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_handbook_entries_content_entries_tenant_id_entry_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_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_handbook_subjects_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_images_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_blueprints_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_question_collections_tenant_id_collecti~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_bank_grants_question_banks_source_question_bank_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_question_banks_tenant_id_question_bank~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_question_collections_tenant_id_co~"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_type_groups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_type_groups_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_videos_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.Content.VideoExplanation", null) - .WithMany() - .HasForeignKey("TenantId", "VideoId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_video_explanations_tenant_id_video_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SourceSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_source_subject_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_created_by"); - - b.HasOne("Tiku.Domain.Content.QuestionBankGrant", null) - .WithMany() - .HasForeignKey("GrantId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_bank_grants_grant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_source_questi~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_collections_tenant_~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "TargetEntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_content_entries_tenant_id_ta~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_tenant_id_tar~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_video_explanations_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_video_explanations_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_units_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_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_vocabulary_units_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_words_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_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_vocabulary_words_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyUnit", null) - .WithMany() - .HasForeignKey("TenantId", "UnitId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_vocabulary_units_tenant_id_unit_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GeneratedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_generated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PaidBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_paid_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.CommissionSettlement", null) - .WithMany() - .HasForeignKey("TenantId", "SettlementId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_commission_settlements_tenant_i~"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_config_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_log_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_queue_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralTrack", null) - .WithMany() - .HasForeignKey("TenantId", "FirstTrackId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_referral_tracks_tenant_id_first_track_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_qrcodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_qrcodes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("LeaderUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_team_edges_users_leader_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("MemberUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_users_member_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("TargetUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_target_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_tracks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralLead", null) - .WithMany() - .HasForeignKey("TenantId", "LeadId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_referral_leads_tenant_id_lead_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_commission_settings_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_commission_settings_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedMajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_majors_tenant_id_selected_major_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedSchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_schools_tenant_id_selected_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_identities_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_pb_import_issues_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_runs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId", "QuestionVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_question_versions_tenant_id_question_id_ques~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "UserId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "UserId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_practice_sessions_tenant_id_user_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_dashboard_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_dashboard_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_exam_dates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_practice_sessions_tenant_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .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.PracticeSessionReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .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") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_practice_sessions_tenant_id_practi~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_sessions_tenant_i~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSessionReport", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_session_reports_t~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_recent_practices_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_recent_practices_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("HandledBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_handled_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_reports_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_report_status_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.Report", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_reports_tenant_id_report_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_revenue_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_revenue_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_announcements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_audit_logs_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_audit_logs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_badges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_banners_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_banners_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_faqs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_faqs_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReadBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_read_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_content_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "SourceQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_tenant_content_notifications_question_banks_tenant_id_sourc~"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("ActiveTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_active_template~"); - - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("DraftTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_draft_template_~"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("DraftUpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_draft_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PublishedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_published_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_theme_configs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GrantedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_badges_users_granted_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_badges_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_users_user_id"); - - b.HasOne("Tiku.Domain.Operations.Badge", null) - .WithMany() - .HasForeignKey("TenantId", "BadgeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_badges_tenant_id_badge_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AcknowledgedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_acknowledged_by"); - - b.HasOne("Tiku.Domain.Operations.AuditLog", null) - .WithMany() - .HasForeignKey("AuditLogId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_audit_logs_audit_log_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_resolved_by"); - - b.HasOne("Tiku.Domain.Platform.PlatformAuditAlertRule", null) - .WithMany() - .HasForeignKey("RuleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_platform_audit_alert_rules_rule_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alerts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alert_rules_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.HasOne("Tiku.Domain.Platform.PlatformDunningNotificationChannel", null) - .WithMany() - .HasForeignKey("ChannelId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_platform_dunning_notif~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoices_tenant~"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoiceReminder", null) - .WithMany() - .HasForeignKey("TenantId", "ReminderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoice_reminde~"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Platform.TenantBillingProfile", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_billing_profiles_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoices_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoices_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReceivedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_payments_users_received_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_reminders_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenant_invoices_tenant_id_invoice_~"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_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_questions_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "PrimaryCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_collections_tenant_id_primary_collection~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_banks_tenant_id_question_bank_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_subjects_tenant_id_subject_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "Id", "CurrentVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_versions_tenant_id_id_current_version_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_banks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_banks_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_versions_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_versions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_login_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_auth_login_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_send_rate_limits_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_verification_codes_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("OwnerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenants_users_owner_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_auth_providers_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantBranding", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_branding_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_classes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_classes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_domains_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantRoleTemplate", null) - .WithMany() - .HasForeignKey("TenantId", "RoleTemplateId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_role_templates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantSettings", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_settings_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CompletedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_completed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_updated_by"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726071027_AddProductActiveAndOperationsCatalog.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726071027_AddProductActiveAndOperationsCatalog.cs deleted file mode 100644 index d0758d3..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726071027_AddProductActiveAndOperationsCatalog.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - /// - public partial class AddProductActiveAndOperationsCatalog : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "ix_products_tenant_id_region_id_type_sort_order", - table: "products"); - - migrationBuilder.AddColumn( - name: "is_active", - table: "products", - type: "boolean", - nullable: false, - defaultValue: true); - - migrationBuilder.CreateIndex( - name: "ix_products_tenant_id_region_id_type_is_active_sort_order", - table: "products", - columns: new[] { "tenant_id", "region_id", "type", "is_active", "sort_order" }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "ix_products_tenant_id_region_id_type_is_active_sort_order", - table: "products"); - - migrationBuilder.DropColumn( - name: "is_active", - table: "products"); - - migrationBuilder.CreateIndex( - name: "ix_products_tenant_id_region_id_type_sort_order", - table: "products", - columns: new[] { "tenant_id", "region_id", "type", "sort_order" }); - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726102813_AddScorelineDynamicRecords.Designer.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726102813_AddScorelineDynamicRecords.Designer.cs deleted file mode 100644 index 1402efb..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726102813_AddScorelineDynamicRecords.Designer.cs +++ /dev/null @@ -1,15554 +0,0 @@ -// -using System; -using System.Text.Json; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using Tiku.Infrastructure.Persistence; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - [DbContext(typeof(TikuDbContext))] - [Migration("20260726102813_AddScorelineDynamicRecords")] - partial class AddScorelineDynamicRecords - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext"); - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "ltree"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("category_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("SvipQuestionLimit") - .HasColumnType("integer") - .HasColumnName("svip_question_limit"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_categories"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_categories_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_categories_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_categories_tenant_id_node_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_categories_tenant_id_subject_id"); - - b.ToTable("categories", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("StudyTips") - .HasColumnType("text") - .HasColumnName("study_tips"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_majors"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_majors_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_majors_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_majors_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_majors_tenant_id_school_id"); - - b.ToTable("majors", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyModuleId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_module_id"); - - b.Property("LegacyParentId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_parent_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_module_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_module_nodes_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_module_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_module_nodes_tenant_id_module_id"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_module_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_module_nodes_tenant_id_region_id"); - - b.ToTable("module_nodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FullName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("full_name"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsHot") - .HasColumnType("boolean") - .HasColumnName("is_hot"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Pinyin") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("pinyin"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_regions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_regions_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_regions_tenant_id_legacy_id"); - - b.ToTable("regions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsPrimarySchoolModule") - .HasColumnType("boolean") - .HasColumnName("is_primary_school_module"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TextColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("text_color"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_region_modules"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_region_modules_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_region_modules_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_region_modules_tenant_id_region_id"); - - b.ToTable("region_modules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ProfessionalExamDate") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("professional_exam_date"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_schools"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_schools_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_schools_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_schools_tenant_id_module_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_schools_tenant_id_region_id"); - - b.ToTable("schools", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineField", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FieldKey") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("field_key"); - - b.Property("FieldName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("field_name"); - - b.Property("FieldType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("field_type"); - - b.Property("IsFilter") - .HasColumnType("boolean") - .HasColumnName("is_filter"); - - b.Property("IsRequired") - .HasColumnType("boolean") - .HasColumnName("is_required"); - - b.Property("IsTrend") - .HasColumnType("boolean") - .HasColumnName("is_trend"); - - b.Property("IsVisible") - .HasColumnType("boolean") - .HasColumnName("is_visible"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Placeholder") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("placeholder"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Unit") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("unit"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_scoreline_fields"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_scoreline_fields_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_scoreline_fields_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "FieldKey") - .IsUnique() - .HasDatabaseName("ix_scoreline_fields_tenant_id_region_id_field_key"); - - b.HasIndex("TenantId", "RegionId", "IsFilter", "SortOrder") - .HasDatabaseName("ix_scoreline_fields_tenant_id_region_id_is_filter_sort_order"); - - b.ToTable("scoreline_fields", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldValues") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_values") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("major_name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SchoolName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("school_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Year") - .HasColumnType("integer") - .HasColumnName("year"); - - b.HasKey("Id") - .HasName("pk_scoreline_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_scoreline_records_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_scoreline_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_scoreline_records_tenant_id_major_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_scoreline_records_tenant_id_school_id"); - - b.HasIndex("TenantId", "Year") - .HasDatabaseName("ix_scoreline_records_tenant_id_year"); - - b.HasIndex("TenantId", "RegionId", "SchoolId", "MajorId", "Year") - .HasDatabaseName("ix_scoreline_records_tenant_id_region_id_school_id_major_id_ye~"); - - b.ToTable("scoreline_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_subjects_tenant_id_module_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_subjects_tenant_id_node_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_subjects_tenant_id_school_id"); - - b.ToTable("subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AgentUserId") - .HasColumnType("uuid") - .HasColumnName("agent_user_id"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponRedemptionId") - .HasColumnType("uuid") - .HasColumnName("coupon_redemption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("IsUsed") - .HasColumnType("boolean") - .HasColumnName("is_used"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("SoldTo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("sold_to"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitPriceCents") - .HasColumnType("integer") - .HasColumnName("unit_price_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UsedBy") - .HasColumnType("uuid") - .HasColumnName("used_by"); - - b.Property("UsedRegionId") - .HasColumnType("uuid") - .HasColumnName("used_region_id"); - - b.HasKey("Id") - .HasName("pk_activation_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_activation_codes_tenant_id_id"); - - b.HasIndex("AgentUserId") - .HasDatabaseName("ix_activation_codes_agent_user_id"); - - b.HasIndex("UsedBy") - .HasDatabaseName("ix_activation_codes_used_by"); - - b.HasIndex("TenantId", "BatchId") - .HasDatabaseName("ix_activation_codes_tenant_id_batch_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_code"); - - b.HasIndex("TenantId", "CouponRedemptionId") - .HasDatabaseName("ix_activation_codes_tenant_id_coupon_redemption_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UsedRegionId") - .HasDatabaseName("ix_activation_codes_tenant_id_used_region_id"); - - b.ToTable("activation_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CampaignName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("campaign_name"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CostPriceCents") - .HasColumnType("integer") - .HasColumnName("cost_price_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("DefaultUnitPriceCents") - .HasColumnType("integer") - .HasColumnName("default_unit_price_cents"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_code_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_code_batches_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_code_batches_tenant_id_legacy_id"); - - b.ToTable("code_batches", null, t => - { - t.HasCheckConstraint("ck_code_batches_amounts", "default_unit_price_cents >= 0 and cost_price_cents >= 0"); - - t.HasCheckConstraint("ck_code_batches_counts", "total_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillDate") - .HasColumnType("date") - .HasColumnName("bill_date"); - - b.Property("BillType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bill_type"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DuplicateCount") - .HasColumnType("integer") - .HasColumnName("duplicate_count"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IgnoredCount") - .HasColumnType("integer") - .HasColumnName("ignored_count"); - - b.Property("MatchedCount") - .HasColumnType("integer") - .HasColumnName("matched_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MismatchCount") - .HasColumnType("integer") - .HasColumnName("mismatch_count"); - - b.Property("MissingLocalCount") - .HasColumnType("integer") - .HasColumnName("missing_local_count"); - - b.Property("MissingProviderCount") - .HasColumnType("integer") - .HasColumnName("missing_provider_count"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source"); - - b.Property("SourceHash") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("source_name"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_batches_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_batches_created_by"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "Provider", "BillDate", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_provider_bill_dat~"); - - b.ToTable("commerce_reconciliation_batches", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_batches_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_batches_counts", "total_count >= 0 and matched_count >= 0 and mismatch_count >= 0 and missing_local_count >= 0 and missing_provider_count >= 0 and duplicate_count >= 0 and ignored_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("AssignedTo") - .HasColumnType("uuid") - .HasColumnName("assigned_to"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("IssueNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_no"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolutionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("resolution_type"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("summary"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issues_tenant_id_id"); - - b.HasIndex("AssignedTo") - .HasDatabaseName("ix_commerce_reconciliation_issues_assigned_to"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_created_by"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_resolved_by"); - - b.HasIndex("TenantId", "IssueNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_issue_no"); - - b.HasIndex("TenantId", "ItemId") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_item_id") - .HasFilter("item_id is not null and status in ('open', 'investigating', 'escalated')"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "PaymentId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_payment_id_created~"); - - b.HasIndex("TenantId", "AssignedTo", "Status", "DueAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_assigned_to_status~"); - - b.HasIndex("TenantId", "BatchId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_batch_id_status_cr~"); - - b.HasIndex("TenantId", "Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_status_severity_cr~"); - - b.ToTable("commerce_reconciliation_issues", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_issues_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("IssueId") - .HasColumnType("uuid") - .HasColumnName("issue_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issue_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issue_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_actor_user_id"); - - b.HasIndex("TenantId", "IssueId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_tenant_id_issue_id_cre~"); - - b.ToTable("commerce_reconciliation_issue_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LocalStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("local_status"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider_status"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("RefundedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("refunded_at"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_items_tenant_id_id"); - - b.HasIndex("BatchId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_items_batch_id_row_no"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "BatchId", "MatchStatus", "RowNo") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_batch_id_match_stat~"); - - b.HasIndex("TenantId", "Provider", "ProviderTradeNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_provider_provider_t~"); - - b.HasIndex("TenantId", "RefundNo", "ProviderRefundNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_no_provider_~"); - - b.ToTable("commerce_reconciliation_items", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_items_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_items_row", "row_no > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_refund_events_actor_user_id"); - - b.HasIndex("TenantId", "RefundRequestId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_events_tenant_id_refund_request_id_created_~"); - - b.ToTable("commerce_refund_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CancelledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("cancelled_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementAction") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entitlement_action"); - - b.Property("FailedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("failed_at"); - - b.Property("FailureReason") - .HasColumnType("text") - .HasColumnName("failure_reason"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("ProcessedBy") - .HasColumnType("uuid") - .HasColumnName("processed_by"); - - b.Property("Provider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("Reason") - .HasColumnType("text") - .HasColumnName("reason"); - - b.Property("RefundNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RequestedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("requested_at") - .HasDefaultValueSql("now()"); - - b.Property("RequestedBy") - .HasColumnType("uuid") - .HasColumnName("requested_by"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SucceededAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("succeeded_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_requests"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_requests_tenant_id_id"); - - b.HasIndex("ProcessedBy") - .HasDatabaseName("ix_commerce_refund_requests_processed_by"); - - b.HasIndex("RequestedBy") - .HasDatabaseName("ix_commerce_refund_requests_requested_by"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commerce_refund_requests_reviewed_by"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundNo") - .IsUnique() - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_refund_no"); - - b.HasIndex("TenantId", "OrderId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_order_id_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_status_created_at"); - - b.ToTable("commerce_refund_requests", null, t => - { - t.HasCheckConstraint("ck_commerce_refund_requests_amount", "amount_cents > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("discount_type"); - - b.Property("DiscountValue") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("discount_value"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MaxUses") - .HasColumnType("integer") - .HasColumnName("max_uses"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("ValidFrom") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_from"); - - b.Property("ValidTo") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_to"); - - b.HasKey("Id") - .HasName("pk_coupons"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupons_tenant_id_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_code"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupons_tenant_id_plan_id"); - - b.ToTable("coupons", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClaimedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("claimed_at"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponId") - .HasColumnType("uuid") - .HasColumnName("coupon_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountAppliedCents") - .HasColumnType("integer") - .HasColumnName("discount_applied_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_coupon_redemptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupon_redemptions_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_coupon_redemptions_user_id"); - - b.HasIndex("TenantId", "CouponId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_coupon_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_region_id"); - - b.HasIndex("TenantId", "OrderId", "UserId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_order_id_user_id") - .HasFilter("order_id is not null"); - - b.HasIndex("TenantId", "UserId", "CouponId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_user_id_coupon_id") - .HasFilter("coupon_id is not null"); - - b.ToTable("coupon_redemptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("entitlement_type"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("LegacySourceId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_source_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("RevokedBy") - .HasColumnType("uuid") - .HasColumnName("revoked_by"); - - b.Property("RevokedReason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("revoked_reason"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("StartsAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at") - .HasDefaultValueSql("now()"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_entitlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_entitlements_tenant_id_id"); - - b.HasIndex("RevokedBy") - .HasDatabaseName("ix_entitlements_revoked_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_entitlements_user_id"); - - b.HasIndex("TenantId", "UserId", "Status", "ExpiresAt") - .HasDatabaseName("ix_entitlements_tenant_id_user_id_status_expires_at"); - - b.ToTable("entitlements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPlanId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_plan_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("OrderNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PayMethod") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_method"); - - b.Property("PayProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_provider"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("ProductName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("product_name"); - - b.Property("ProductType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("product_type"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("trade_no"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_orders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_orders_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_orders_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderNo") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_order_no"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_orders_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_orders_tenant_id_region_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_orders_tenant_id_user_id_created_at"); - - b.ToTable("orders", null, t => - { - t.HasCheckConstraint("ck_orders_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_orders_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("Quantity") - .HasColumnType("integer") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalAmountCents") - .HasColumnType("integer") - .HasColumnName("total_amount_cents"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_order_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_order_items_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_order_items_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_order_items_tenant_id_order_id"); - - b.ToTable("order_items", null, t => - { - t.HasCheckConstraint("ck_order_items_amounts", "unit_amount_cents >= 0 and total_amount_cents >= 0"); - - t.HasCheckConstraint("ck_order_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyOrderId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_order_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payments_tenant_id_id"); - - b.HasIndex("Provider", "ProviderTradeNo") - .IsUnique() - .HasDatabaseName("ix_payments_provider_provider_trade_no") - .HasFilter("provider_trade_no is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_payments_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId", "UpdatedAt") - .HasDatabaseName("ix_payments_tenant_id_order_id_updated_at"); - - b.ToTable("payments", null, t => - { - t.HasCheckConstraint("ck_payments_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_payments_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("EventId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("event_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("SignatureValid") - .HasColumnType("boolean") - .HasColumnName("signature_valid"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_payment_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payment_events_tenant_id_id"); - - b.HasIndex("Provider", "EventId") - .IsUnique() - .HasDatabaseName("ix_payment_events_provider_event_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_payment_events_tenant_id_payment_id"); - - b.ToTable("payment_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Cover") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cover"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DetailImages") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("detail_images") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true) - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPriceText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_price_text"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("PreviewIframe") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_iframe"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_products"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_products_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_products_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "Type", "IsActive", "SortOrder") - .HasDatabaseName("ix_products_tenant_id_region_id_type_is_active_sort_order"); - - b.ToTable("products", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Badge") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("badge"); - - b.Property("CouponOnly") - .HasColumnType("boolean") - .HasColumnName("coupon_only"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OriginalPriceCents") - .HasColumnType("integer") - .HasColumnName("original_price_cents"); - - b.Property("PerDayLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("per_day_label"); - - b.Property("PriceCents") - .HasColumnType("integer") - .HasColumnName("price_cents"); - - b.Property("Recommended") - .HasColumnType("boolean") - .HasColumnName("recommended"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VpEnabled") - .HasColumnType("boolean") - .HasColumnName("vp_enabled"); - - b.Property("VpProductId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("vp_product_id"); - - b.HasKey("Id") - .HasName("pk_svip_plans"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_svip_plans_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_svip_plans_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_svip_plans_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("svip_plans", null, t => - { - t.HasCheckConstraint("ck_svip_plans_days", "days >= 0"); - - t.HasCheckConstraint("ck_svip_plans_price", "price_cents >= 0 and (original_price_cents is null or original_price_cents >= 0)"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_payment_accounts"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_payment_accounts_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_payment_accounts_tenant_id_provider"); - - b.ToTable("tenant_payment_accounts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillingCycle") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("billing_cycle"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PlanCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("plan_code"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_subscriptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_subscriptions_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "ExpiresAt") - .HasDatabaseName("ix_tenant_subscriptions_tenant_id_status_expires_at"); - - b.ToTable("tenant_subscriptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MetricKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("metric_key"); - - b.Property("MetricValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("metric_value"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_tenant_usage_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_usage_records_tenant_id_id"); - - b.HasIndex("TenantId", "MetricKey", "PeriodStart", "PeriodEnd") - .HasDatabaseName("ix_tenant_usage_records_tenant_id_metric_key_period_start_peri~"); - - b.ToTable("tenant_usage_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContextPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("context_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("GeneratedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("generated_at"); - - b.Property("InputPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("input_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Model") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("model"); - - b.Property("PromptVersion") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("prompt_version"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("ResultPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("result_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_ai_recommendation_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_ai_recommendation_reports_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_ai_recommendation_reports_user_id"); - - b.HasIndex("TenantId", "RegionId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_region_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_user_id_created_at"); - - b.ToTable("ai_recommendation_reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetKey") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_app_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_app_assets_tenant_id_id"); - - b.HasIndex("TenantId", "AssetKey") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_asset_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_legacy_id"); - - b.ToTable("app_assets", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssetKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("AssetType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("asset_type"); - - b.Property("Bucket") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bucket"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("ChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("checksum_sha256"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DownloadCount") - .HasColumnType("integer") - .HasColumnName("download_count"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("FileSizeBytes") - .HasColumnType("bigint") - .HasColumnName("file_size_bytes"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MimeType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mime_type"); - - b.Property("ObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("object_key"); - - b.Property("PreviewObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("preview_object_key"); - - b.Property("PreviewStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("preview_status"); - - b.Property("PreviewUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_url"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SecurityFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScanProvider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("security_scan_provider"); - - b.Property("SecurityScanStatus") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("not_required") - .HasColumnName("security_scan_status"); - - b.Property("SecurityScanSummary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_scan_summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScannedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("security_scanned_at"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StorageProvider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("storage_provider"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UploadStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("upload_status"); - - b.Property("VerificationDetails") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("verification_details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.Property("VerifiedBy") - .HasColumnType("uuid") - .HasColumnName("verified_by"); - - b.Property("VerifiedChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("verified_checksum_sha256"); - - b.Property("VerifiedSizeBytes") - .HasColumnType("bigint") - .HasColumnName("verified_size_bytes"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_assets_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_assets_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_assets_updated_by"); - - b.HasIndex("VerifiedBy") - .HasDatabaseName("ix_content_assets_verified_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_content_assets_tenant_id_category_id"); - - b.HasIndex("TenantId", "ChecksumSha256") - .HasDatabaseName("ix_content_assets_tenant_id_checksum_sha256") - .HasFilter("checksum_sha256 is not null"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_content_assets_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_assets_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PreviewStatus") - .HasDatabaseName("ix_content_assets_tenant_id_preview_status") - .HasFilter("preview_status <> 'none'"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_assets_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_content_assets_tenant_id_subject_id"); - - b.HasIndex("TenantId", "StorageProvider", "Bucket", "ObjectKey") - .HasDatabaseName("ix_content_assets_tenant_id_storage_provider_bucket_object_key"); - - b.HasIndex("TenantId", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_upload_status_status_updated_at"); - - b.HasIndex("TenantId", "SecurityScanStatus", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_security_scan_status_upload_status~"); - - b.HasIndex("TenantId", "Status", "Visibility", "AssetType", "RegionId", "SubjectId", "CategoryId", "SortOrder") - .HasDatabaseName("ix_content_assets_tenant_id_status_visibility_asset_type_regio~"); - - b.ToTable("content_assets", null, t => - { - t.HasCheckConstraint("ck_content_assets_download_count", "download_count >= 0"); - - t.HasCheckConstraint("ck_content_assets_file_size", "file_size_bytes is null or file_size_bytes >= 0"); - - t.HasCheckConstraint("ck_content_assets_verified_checksum", "verified_checksum_sha256 is null or verified_checksum_sha256 ~ '^[a-f0-9]{64}$'"); - - t.HasCheckConstraint("ck_content_assets_verified_size", "verified_size_bytes is null or verified_size_bytes >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_type"); - - b.Property("ActorRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("actor_role"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("AssetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("asset_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DenyCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("deny_code"); - - b.Property("Disposition") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("disposition"); - - b.Property("ExpiresInSeconds") - .HasColumnType("integer") - .HasColumnName("expires_in_seconds"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("SignatureMode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("signature_mode"); - - b.Property("StorageProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("storage_provider"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("Visibility") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_asset_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_content_asset_access_events_user_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_asset_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_user_id_created_at") - .HasFilter("user_id is not null"); - - b.HasIndex("TenantId", "AccessType", "Result", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_access_type_result_cr~"); - - b.ToTable("content_asset_access_events", null, t => - { - t.HasCheckConstraint("ck_content_asset_access_events_expiry", "expires_in_seconds is null or expires_in_seconds > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.PrimitiveCollection("IssueCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("issue_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RiskLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("risk_level"); - - b.Property("ScanStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scan_status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_content_asset_security_scan_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_security_scan_events_tenant_id_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_asset_id_creat~"); - - b.HasIndex("TenantId", "ScanStatus", "RiskLevel", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_scan_status_ri~"); - - b.ToTable("content_asset_security_scan_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("description"); - - b.Property("EntryKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("entry_key"); - - b.Property("EntryType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entry_type"); - - b.Property("Icon") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LayoutConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("layout_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_entries_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_entries_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_entries_updated_by"); - - b.HasIndex("TenantId", "EntryKey") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_entry_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "EntryType", "IsActive", "SortOrder") - .HasDatabaseName("ix_content_entries_tenant_id_region_id_entry_type_is_active_so~"); - - b.ToTable("content_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("Message") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("message"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "ItemId") - .HasDatabaseName("ix_content_import_issues_tenant_id_item_id"); - - b.HasIndex("TenantId", "JobId", "Severity", "RowNo") - .HasDatabaseName("ix_content_import_issues_tenant_id_job_id_severity_row_no"); - - b.ToTable("content_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("content_hash"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExternalId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("external_id"); - - b.Property("IssuesCount") - .HasColumnType("integer") - .HasColumnName("issues_count"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("SourcePayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_items_tenant_id_id"); - - b.HasIndex("JobId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_content_import_items_job_id_row_no"); - - b.HasIndex("TenantId", "JobId", "Status", "RowNo") - .HasDatabaseName("ix_content_import_items_tenant_id_job_id_status_row_no"); - - b.ToTable("content_import_items", null, t => - { - t.HasCheckConstraint("ck_content_import_items_issues_count", "issues_count >= 0"); - - t.HasCheckConstraint("ck_content_import_items_row_no", "row_no >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DryRun") - .HasColumnType("boolean") - .HasColumnName("dry_run"); - - b.Property("ErrorCount") - .HasColumnType("integer") - .HasColumnName("error_count"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("ImportType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("import_type"); - - b.Property("InsertedCount") - .HasColumnType("integer") - .HasColumnName("inserted_count"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SkippedCount") - .HasColumnType("integer") - .HasColumnName("skipped_count"); - - b.Property("SourceFormat") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_format"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TargetCategoryId") - .HasColumnType("uuid") - .HasColumnName("target_category_id"); - - b.Property("TargetContentNodeId") - .HasColumnType("uuid") - .HasColumnName("target_content_node_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TargetRegionId") - .HasColumnType("uuid") - .HasColumnName("target_region_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedCount") - .HasColumnType("integer") - .HasColumnName("updated_count"); - - b.Property("ValidCount") - .HasColumnType("integer") - .HasColumnName("valid_count"); - - b.Property("WarningCount") - .HasColumnType("integer") - .HasColumnName("warning_count"); - - b.HasKey("Id") - .HasName("pk_content_import_jobs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_jobs_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_import_jobs_created_by"); - - b.HasIndex("TenantId", "TargetCategoryId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_category_id"); - - b.HasIndex("TenantId", "TargetContentNodeId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_content_node_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_question_bank_id"); - - b.HasIndex("TenantId", "TargetRegionId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_region_id"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_subject_id"); - - b.HasIndex("TenantId", "ImportType", "Status", "CreatedAt") - .HasDatabaseName("ix_content_import_jobs_tenant_id_import_type_status_created_at"); - - b.ToTable("content_import_jobs", null, t => - { - t.HasCheckConstraint("ck_content_import_jobs_counts", "total_count >= 0 and valid_count >= 0 and error_count >= 0 and warning_count >= 0 and inserted_count >= 0 and updated_count >= 0 and skipped_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Depth") - .HasColumnType("integer") - .HasColumnName("depth"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsLeaf") - .HasColumnType("boolean") - .HasColumnName("is_leaf"); - - b.Property("IsSelectable") - .HasColumnType("boolean") - .HasColumnName("is_selectable"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MarkerConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("marker_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MarkerType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("marker_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("node_key"); - - b.Property("NodeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("node_type"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasColumnType("ltree") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_content_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_nodes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_nodes_created_by"); - - b.HasIndex("Path") - .HasDatabaseName("ix_content_nodes_path"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Path"), "gist"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_nodes_updated_by"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MarkerType") - .HasDatabaseName("ix_content_nodes_tenant_id_marker_type") - .HasFilter("marker_type is not null"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_content_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_nodes_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "NodeKey") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_node_key"); - - b.HasIndex("TenantId", "EntryId", "ParentId", "SortOrder") - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_parent_id_sort_order"); - - b.ToTable("content_nodes", null, t => - { - t.HasCheckConstraint("ck_content_nodes_depth", "depth >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_chapters"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_chapters_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_chapters_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_subject_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_chapters_tenant_id_entry_id_content_node_id_subjec~"); - - b.ToTable("handbook_chapters", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChapterId") - .HasColumnType("uuid") - .HasColumnName("chapter_id"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_entries_tenant_id_id"); - - b.HasIndex("TenantId", "ChapterId") - .HasDatabaseName("ix_handbook_entries_tenant_id_chapter_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_entries_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "ChapterId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_entries_tenant_id_entry_id_content_node_id_chapter~"); - - b.ToTable("handbook_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_school_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_subjects_tenant_id_entry_id_content_node_id_region~"); - - b.ToTable("handbook_subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_images"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_images_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_images_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsPublic") - .HasDatabaseName("ix_images_tenant_id_category_is_public"); - - b.ToTable("images", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssemblyType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assembly_type"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PassScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("pass_score"); - - b.Property("QuestionLimit") - .HasColumnType("integer") - .HasColumnName("question_limit"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Rules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Sections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sections") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_practice_blueprints"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_blueprints_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_practice_blueprints_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_practice_blueprints_updated_by"); - - b.HasIndex("TenantId", "CollectionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_collection_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_practice_blueprints_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_region_id"); - - b.HasIndex("TenantId", "NodeId", "CollectionId", "Mode", "Status", "SortOrder") - .HasDatabaseName("ix_practice_blueprints_tenant_id_node_id_collection_id_mode_st~"); - - b.ToTable("practice_blueprints", null, t => - { - t.HasCheckConstraint("ck_practice_blueprints_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_practice_blueprints_question_limit", "question_limit is null or question_limit > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("AllowedPlanCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("allowed_plan_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.PrimitiveCollection("AllowedRegionIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_region_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedSubjectIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_subject_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedTenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("GrantScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("grant_scope"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_bank_grants"); - - b.HasIndex("AllowedPlanCodes") - .HasDatabaseName("ix_question_bank_grants_allowed_plan_codes"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedPlanCodes"), "gin"); - - b.HasIndex("AllowedTenantIds") - .HasDatabaseName("ix_question_bank_grants_allowed_tenant_ids"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedTenantIds"), "gin"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_bank_grants_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_bank_grants_updated_by"); - - b.HasIndex("SourceQuestionBankId", "Status", "StartsAt", "ExpiresAt") - .HasDatabaseName("ix_question_bank_grants_source_question_bank_id_status_starts_~"); - - b.ToTable("question_bank_grants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CollectionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("collection_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Filters") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("filters") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_collections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collections_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_collections_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_collections_updated_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_question_collections_tenant_id_category_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_question_collections_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_collections_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_question_collections_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_collections_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_question_collections_tenant_id_subject_id"); - - b.HasIndex("TenantId", "NodeId", "Status", "SortOrder") - .HasDatabaseName("ix_question_collections_tenant_id_node_id_status_sort_order"); - - b.ToTable("question_collections", null, t => - { - t.HasCheckConstraint("ck_question_collections_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_question_collections_question_count", "question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Required") - .HasColumnType("boolean") - .HasColumnName("required"); - - b.Property("Score") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_collection_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collection_items_tenant_id_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_question_collection_items_tenant_id_question_id"); - - b.HasIndex("TenantId", "CollectionId", "QuestionId") - .IsUnique() - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_question_~"); - - b.HasIndex("TenantId", "CollectionId", "SectionKey", "SortOrder") - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_section_k~"); - - b.ToTable("question_collection_items", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Types") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("types") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_type_groups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_type_groups_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_type_groups_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_question_type_groups_tenant_id_subject_id_is_active_sort_or~"); - - b.ToTable("question_type_groups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("LegacyVideoId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_video_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoId") - .HasColumnType("uuid") - .HasColumnName("video_id"); - - b.Property("VideoType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("video_type"); - - b.HasKey("Id") - .HasName("pk_question_videos"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_videos_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "VideoId") - .HasDatabaseName("ix_question_videos_tenant_id_video_id"); - - b.HasIndex("TenantId", "QuestionId", "VideoId", "VideoType") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_question_id_video_id_video_type"); - - b.ToTable("question_videos", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("SourceSubjectId") - .HasColumnType("uuid") - .HasColumnName("source_subject_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacySourceSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_source_subject_id"); - - b.Property("LegacyTargetSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_target_subject_id"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.HasKey("TenantId", "SourceSubjectId", "TargetSubjectId") - .HasName("pk_subject_shares"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_subject_shares_tenant_id_target_subject_id"); - - b.ToTable("subject_shares", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AdoptionMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("adoption_mode"); - - b.Property("CopiedQuestionCount") - .HasColumnType("integer") - .HasColumnName("copied_question_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("GrantId") - .HasColumnType("uuid") - .HasColumnName("grant_id"); - - b.Property("LastSyncedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_synced_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("SourceSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SyncStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("sync_status"); - - b.Property("TargetCollectionId") - .HasColumnType("uuid") - .HasColumnName("target_collection_id"); - - b.Property("TargetEntryId") - .HasColumnType("uuid") - .HasColumnName("target_entry_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_question_bank_adoptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_question_bank_adoptions_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_created_by"); - - b.HasIndex("GrantId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_grant_id"); - - b.HasIndex("SourceQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_source_question_bank_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_updated_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .IsUnique() - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_source_question_ba~"); - - b.HasIndex("TenantId", "TargetCollectionId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_collection_~"); - - b.HasIndex("TenantId", "TargetEntryId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_entry_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_question_ba~"); - - b.HasIndex("TenantId", "Status", "UpdatedAt") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_status_updated_at"); - - b.ToTable("tenant_question_bank_adoptions", null, t => - { - t.HasCheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FavoritedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("favorited_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Note") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.HasKey("Id") - .HasName("pk_user_word_favorites"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_favorites_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_favorites_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_favorites_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_favorites_tenant_id_user_id_word_id"); - - b.ToTable("user_word_favorites", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CorrectStreak") - .HasColumnType("integer") - .HasColumnName("correct_streak"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DueLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("due_level"); - - b.Property("EaseFactor") - .ValueGeneratedOnAdd() - .HasPrecision(4, 2) - .HasColumnType("numeric(4,2)") - .HasDefaultValue(2.50m) - .HasColumnName("ease_factor"); - - b.Property("LastResult") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("last_result"); - - b.Property("LastReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_review_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_review_at"); - - b.Property("ReviewCount") - .HasColumnType("integer") - .HasColumnName("review_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_user_word_progress"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_progress_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_progress_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_progress_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_word_id"); - - b.HasIndex("TenantId", "UserId", "NextReviewAt", "Status", "DueLevel") - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_next_review_at_status_~"); - - b.ToTable("user_word_progress", null, t => - { - t.HasCheckConstraint("ck_user_word_progress_correct_streak", "correct_streak >= 0"); - - t.HasCheckConstraint("ck_user_word_progress_ease_factor", "ease_factor >= 1.30 and ease_factor <= 3.00"); - - t.HasCheckConstraint("ck_user_word_progress_review_count", "review_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsGeneral") - .HasColumnType("boolean") - .HasColumnName("is_general"); - - b.Property("KnowledgeTags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("knowledge_tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ThumbnailUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("thumbnail_url"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("video_url"); - - b.HasKey("Id") - .HasName("pk_video_explanations"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_video_explanations_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_video_explanations_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SourceHash") - .HasDatabaseName("ix_video_explanations_tenant_id_source_hash") - .HasFilter("source_hash is not null"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_video_explanations_tenant_id_subject_id_is_active_sort_order"); - - b.ToTable("video_explanations", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WordCount") - .HasColumnType("integer") - .HasColumnName("word_count"); - - b.HasKey("Id") - .HasName("pk_vocabulary_units"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_units_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_units_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_units_tenant_id_entry_id_content_node_id_region_~"); - - b.ToTable("vocabulary_units", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Example") - .HasColumnType("text") - .HasColumnName("example"); - - b.Property("ExampleTranslation") - .HasColumnType("text") - .HasColumnName("example_translation"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Meaning") - .HasColumnType("text") - .HasColumnName("meaning"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phonetic") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("phonetic"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitId") - .HasColumnType("uuid") - .HasColumnName("unit_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Word") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("word"); - - b.HasKey("Id") - .HasName("pk_vocabulary_words"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_words_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_words_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_words_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UnitId", "IsActive", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_vocabulary_words_tenant_id_unit_id_is_active_sort_order_cre~"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "UnitId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_words_tenant_id_entry_id_content_node_id_unit_id~"); - - b.ToTable("vocabulary_words", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("EffectiveRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("effective_rate"); - - b.Property("GeneratedBy") - .HasColumnType("uuid") - .HasColumnName("generated_by"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidBy") - .HasColumnType("uuid") - .HasColumnName("paid_by"); - - b.Property("PaidUserCount") - .HasColumnType("integer") - .HasColumnName("paid_user_count"); - - b.Property("PaymentAccount") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("payment_account"); - - b.Property("PaymentMethod") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_method"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("SettlementNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("settlement_no"); - - b.Property("SourceCount") - .HasColumnType("integer") - .HasColumnName("source_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlements_tenant_id_id"); - - b.HasIndex("GeneratedBy") - .HasDatabaseName("ix_commission_settlements_generated_by"); - - b.HasIndex("PaidBy") - .HasDatabaseName("ix_commission_settlements_paid_by"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlements_referrer_user_id"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commission_settlements_reviewed_by"); - - b.HasIndex("TenantId", "SettlementNo") - .IsUnique() - .HasDatabaseName("ix_commission_settlements_tenant_id_settlement_no"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart") - .HasDatabaseName("ix_commission_settlements_referrer_start"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart", "PeriodEnd") - .IsUnique() - .HasDatabaseName("ux_commission_settlements_referrer_period"); - - b.ToTable("commission_settlements", null, t => - { - t.HasCheckConstraint("ck_commission_settlements_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_counts", "source_count >= 0 and paid_user_count >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_period", "period_end >= period_start"); - - t.HasCheckConstraint("ck_commission_settlements_rates", "default_rate >= 0 and default_rate <= 1 and (effective_rate is null or (effective_rate >= 0 and effective_rate <= 1))"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AttributionType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("attribution_type"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RateSource") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("rate_source"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("SettlementId") - .HasColumnType("uuid") - .HasColumnName("settlement_id"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_no"); - - b.Property("SourcePaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("source_paid_at"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlement_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlement_items_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlement_items_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_commission_settlement_items_student_user_id"); - - b.HasIndex("TenantId", "SettlementId") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_settlement_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "SourcePaidAt") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_referrer_user_id_sour~"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .IsUnique() - .HasDatabaseName("ix_commission_settlement_items_tenant_id_source_type_source_id"); - - b.ToTable("commission_settlement_items", null, t => - { - t.HasCheckConstraint("ck_commission_settlement_items_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlement_items_rate", "commission_rate >= 0 and commission_rate <= 1"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignmentConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssignmentCursor") - .HasColumnType("integer") - .HasColumnName("assignment_cursor"); - - b.Property("AssignmentMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("AssignmentPool") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_pool") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DelaySeconds") - .HasColumnType("integer") - .HasColumnName("delay_seconds"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("ExamType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("exam_type"); - - b.Property("FormName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("form_name"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Url") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("url"); - - b.HasKey("Id") - .HasName("pk_crm_config"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_config_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_crm_config_tenant_id"); - - b.HasIndex("TenantId", "AssignmentMode") - .HasDatabaseName("ix_crm_config_tenant_id_assignment_mode"); - - b.ToTable("crm_config", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempt") - .HasColumnType("integer") - .HasColumnName("attempt"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("HttpCode") - .HasColumnType("integer") - .HasColumnName("http_code"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Outcome") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("outcome"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("RequestBody") - .HasColumnType("text") - .HasColumnName("request_body"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ResponseSummary") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("response_summary"); - - b.Property("SignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("signed_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_log"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_log_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_log_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_log_tenant_id_record_id_created_at"); - - b.ToTable("crm_webhook_log", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("ScheduledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Source") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("target_url"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_queue"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_queue_tenant_id_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_idempotency_key") - .HasFilter("idempotency_key is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id_created_at") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "Source", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_source_created_at") - .HasFilter("source = 'tenant.student.crm_push'"); - - b.HasIndex("TenantId", "Status", "ScheduledAt", "NextAttemptAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_status_scheduled_at_next_attemp~"); - - b.ToTable("crm_webhook_queue", null, t => - { - t.HasCheckConstraint("ck_crm_webhook_queue_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LandingPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("landing_path"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_codes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_codes_user_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_code"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_user_id"); - - b.HasIndex("TenantId", "Code", "Status") - .HasDatabaseName("ix_referral_codes_tenant_id_code_status"); - - b.ToTable("referral_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("assigned_at"); - - b.Property("AssignedBy") - .HasColumnType("uuid") - .HasColumnName("assigned_by"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("AssignmentMode") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("BindType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bind_type"); - - b.Property("BoundAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("bound_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FirstTrackId") - .HasColumnType("uuid") - .HasColumnName("first_track_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ProtectedUntil") - .HasColumnType("timestamp with time zone") - .HasColumnName("protected_until"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_leads"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_leads_tenant_id_id"); - - b.HasIndex("AssignedBy") - .HasDatabaseName("ix_referral_leads_assigned_by"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_referral_leads_assigned_to_user_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_leads_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_referral_leads_student_user_id"); - - b.HasIndex("TenantId", "FirstTrackId") - .HasDatabaseName("ix_referral_leads_tenant_id_first_track_id"); - - b.HasIndex("TenantId", "StudentUserId") - .IsUnique() - .HasDatabaseName("ix_referral_leads_tenant_id_student_user_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_referrer_user_id_bound_at"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_assigned_to_user_id_status_bound_at"); - - b.ToTable("referral_leads", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Page") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("page"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("QrcodeUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("qrcode_url"); - - b.Property("RefCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("Scene") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("scene"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_qrcodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_qrcodes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_qrcodes_user_id"); - - b.HasIndex("TenantId", "RefCode", "Status") - .HasDatabaseName("ix_referral_qrcodes_tenant_id_ref_code_status"); - - b.HasIndex("TenantId", "Provider", "Scene", "Page") - .IsUnique() - .HasDatabaseName("ix_referral_qrcodes_tenant_id_provider_scene_page"); - - b.ToTable("referral_qrcodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LeaderUserId") - .HasColumnType("uuid") - .HasColumnName("leader_user_id"); - - b.Property("MemberUserId") - .HasColumnType("uuid") - .HasColumnName("member_user_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RelationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("relation_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_team_edges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_team_edges_tenant_id_id"); - - b.HasIndex("LeaderUserId") - .HasDatabaseName("ix_referral_team_edges_leader_user_id"); - - b.HasIndex("MemberUserId") - .HasDatabaseName("ix_referral_team_edges_member_user_id"); - - b.HasIndex("TenantId", "LeaderUserId", "Status") - .HasDatabaseName("ix_referral_team_edges_tenant_id_leader_user_id_status"); - - b.HasIndex("TenantId", "MemberUserId", "RelationType") - .IsUnique() - .HasDatabaseName("ix_referral_team_edges_tenant_id_member_user_id_relation_type"); - - b.ToTable("referral_team_edges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("LeadId") - .HasColumnType("uuid") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("TargetUserId") - .HasColumnType("uuid") - .HasColumnName("target_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_referral_tracks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_tracks_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_tracks_referrer_user_id"); - - b.HasIndex("TargetUserId") - .HasDatabaseName("ix_referral_tracks_target_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_referral_tracks_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "LeadId", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_lead_id_created_at"); - - b.HasIndex("TenantId", "RefCode", "EventType", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_ref_code_event_type_created_at"); - - b.ToTable("referral_tracks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("MinSettlementCents") - .HasColumnType("integer") - .HasColumnName("min_settlement_cents"); - - b.Property("SettlementCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("settlement_cycle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_commission_settings"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_commission_settings_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_commission_settings_tenant_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_commission_settings_updated_by"); - - b.ToTable("tenant_commission_settings", null, t => - { - t.HasCheckConstraint("ck_tenant_commission_settings_default_rate", "default_rate >= 0 and default_rate <= 1"); - - t.HasCheckConstraint("ck_tenant_commission_settings_min_settlement", "min_settlement_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastCheckInDate") - .HasColumnType("date") - .HasColumnName("last_check_in_date"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("MasteredWordsCount") - .HasColumnType("integer") - .HasColumnName("mastered_words_count"); - - b.Property("ModuleSelections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_selections") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Progress") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("progress") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionsAnsweredToday") - .HasColumnType("integer") - .HasColumnName("questions_answered_today"); - - b.Property("RecentActivities") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("recent_activities") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SelectedMajorId") - .HasColumnType("uuid") - .HasColumnName("selected_major_id"); - - b.Property("SelectedSchoolId") - .HasColumnType("uuid") - .HasColumnName("selected_school_id"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_student_profiles"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_student_profiles_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_student_profiles_user_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_student_profiles_tenant_id_region_id"); - - b.HasIndex("TenantId", "SelectedMajorId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_major_id"); - - b.HasIndex("TenantId", "SelectedSchoolId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_school_id"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_student_profiles_tenant_id_user_id"); - - b.ToTable("student_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AvatarUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("avatar_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("LastSeenAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPasswordHash") - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasColumnName("legacy_password_hash"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PasswordMigrationRequired") - .HasColumnType("boolean") - .HasColumnName("password_migration_required"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("PrimaryRole") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("primary_role"); - - b.Property("RawProfile") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_profile") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Score") - .HasColumnType("integer") - .HasColumnName("score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Username") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Email") - .IsUnique() - .HasDatabaseName("ix_users_email"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_users_legacy_id"); - - b.HasIndex("Phone") - .IsUnique() - .HasDatabaseName("ix_users_phone"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("OpenId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("open_id"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderSubject") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("provider_subject"); - - b.Property("SecretPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("secret_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UnionId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("union_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_identities"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_identities_user_id"); - - b.HasIndex("Provider", "ProviderSubject") - .IsUnique() - .HasDatabaseName("ix_user_identities_provider_provider_subject"); - - b.ToTable("user_identities", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("IssueCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("RawValueSample") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("raw_value_sample"); - - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_run_id"); - - b.HasIndex("RunId", "Severity", "CollectionName") - .HasDatabaseName("ix_pb_import_issues_run_id_severity_collection_name"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("SourceKind") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_kind"); - - b.Property("SourceName") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_runs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_runs_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "StartedAt") - .HasDatabaseName("ix_pb_import_runs_tenant_id_status_started_at"); - - b.ToTable("pb_import_runs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("CollectionName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Errors") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("errors") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ImportedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("imported_at") - .HasDefaultValueSql("now()"); - - b.Property("Normalized") - .HasColumnType("boolean") - .HasColumnName("normalized"); - - b.Property("Record") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("record") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("RunId", "CollectionName", "LegacyId") - .HasName("pk_pb_raw_records"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_run_id"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_raw_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("AnsweredAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("answered_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsCorrect") - .HasColumnType("boolean") - .HasColumnName("is_correct"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("QuestionVersionId") - .HasColumnType("uuid") - .HasColumnName("question_version_id"); - - b.Property("SelectedOptions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("selected_options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_answer_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_answer_records_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_answer_records_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_answer_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id"); - - b.HasIndex("TenantId", "QuestionId", "QuestionVersionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id_question_version_id"); - - b.HasIndex("TenantId", "UserId", "PracticeSessionId") - .HasDatabaseName("ix_answer_records_tenant_id_user_id_practice_session_id"); - - b.ToTable("answer_records", null, t => - { - t.HasCheckConstraint("ck_answer_records_version_requires_question", "question_version_id is null or question_id is not null"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActiveUsers") - .HasColumnType("integer") - .HasColumnName("active_users"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("NewOrders") - .HasColumnType("integer") - .HasColumnName("new_orders"); - - b.Property("NewQuestions") - .HasColumnType("integer") - .HasColumnName("new_questions"); - - b.Property("NewRevenueCents") - .HasColumnType("integer") - .HasColumnName("new_revenue_cents"); - - b.Property("NewUsers") - .HasColumnType("integer") - .HasColumnName("new_users"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_dashboard_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_dashboard_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_stat_date_region_id"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId"), false); - - b.ToTable("dashboard_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("ExamAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("exam_at"); - - b.Property("ExamName") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("exam_name"); - - b.Property("ExamType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("exam_type"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_exam_dates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_exam_dates_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_exam_dates_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_exam_dates_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId", "ExamAt") - .HasDatabaseName("ix_exam_dates_tenant_id_school_id_exam_at"); - - b.ToTable("exam_dates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_favorite_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_favorite_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_favorite_questions_tenant_id_question_id"); - - b.ToTable("favorite_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_mode"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementId") - .HasColumnType("uuid") - .HasColumnName("entitlement_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("GrantedCount") - .HasColumnType("integer") - .HasColumnName("granted_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("Reason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("reason"); - - b.Property("RequestedCount") - .HasColumnType("integer") - .HasColumnName("requested_count"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_access_events_user_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_access_events_tenant_id_practice_session_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_practice_access_events_tenant_id_user_id_created_at"); - - b.ToTable("practice_access_events", null, t => - { - t.HasCheckConstraint("ck_practice_access_events_consumed_free_quota", "consumed_free_quota >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_granted_count", "granted_count >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_requested_count", "requested_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FreeLimit") - .HasColumnType("integer") - .HasColumnName("free_limit"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsageDate") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasColumnName("usage_date") - .HasDefaultValueSql("current_date"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_daily_usage"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_daily_usage_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_daily_usage_user_id"); - - b.HasIndex("TenantId", "UserId", "UsageDate") - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date"); - - b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId") - .IsUnique() - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date_scope_typ~"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId"), false); - - b.ToTable("practice_daily_usage", null, t => - { - t.HasCheckConstraint("ck_practice_daily_usage_free_limit", "free_limit >= 0"); - - t.HasCheckConstraint("ck_practice_daily_usage_used_count", "used_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessEntitlementId") - .HasColumnType("uuid") - .HasColumnName("access_entitlement_id"); - - b.Property("AccessMode") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("free") - .HasColumnName("access_mode"); - - b.Property("AccessSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_sessions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "UserId", "Id") - .HasName("ak_practice_sessions_tenant_id_user_id_id"); - - 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"); - - b.ToTable("practice_sessions", null, t => - { - t.HasCheckConstraint("ck_practice_sessions_consumed_free_quota", "consumed_free_quota >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionResults") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_results") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionStats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("section_stats") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("SubmittedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("submitted_at") - .HasDefaultValueSql("now()"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalQuestions") - .HasColumnType("integer") - .HasColumnName("total_questions"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.Property("WrongQuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("wrong_question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.HasKey("Id") - .HasName("pk_practice_session_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_reports_tenant_id_id"); - - 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.ToTable("practice_session_reports", null, t => - { - t.HasCheckConstraint("ck_practice_session_reports_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_reports_counts", "total_questions >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_duration", "duration_seconds >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("question_type"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SectionName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("section_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_practice_session_report_sections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_report_sections_tenant_id_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_practice_session~"); - - b.HasIndex("TenantId", "ReportId", "SectionKey") - .IsUnique() - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sectio~"); - - b.HasIndex("TenantId", "ReportId", "SortOrder") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sort_o~"); - - b.ToTable("practice_session_report_sections", null, t => - { - t.HasCheckConstraint("ck_practice_session_report_sections_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_report_sections_counts", "question_count >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_report_sections_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastAccessAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_access_at"); - - b.Property("LastPracticeAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_practice_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("practice_type"); - - b.Property("Progress") - .HasColumnType("integer") - .HasColumnName("progress"); - - b.Property("TargetLegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("target_legacy_id"); - - b.Property("TargetName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("target_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_recent_practices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_recent_practices_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_recent_practices_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_recent_practices_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "LastAccessAt") - .HasDatabaseName("ix_recent_practices_tenant_id_user_id_last_access_at"); - - b.ToTable("recent_practices", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attachments") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("attachments") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("Contact") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("contact"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("HandledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("handled_at"); - - b.Property("HandledBy") - .HasColumnType("uuid") - .HasColumnName("handled_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Resolution") - .HasColumnType("text") - .HasColumnName("resolution"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_reports_tenant_id_id"); - - b.HasIndex("HandledBy") - .HasDatabaseName("ix_reports_handled_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_reports_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_reports_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_question_id_created_at") - .HasFilter("question_id is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_status_created_at"); - - b.ToTable("reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_report_status_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_report_status_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_report_status_events_actor_user_id"); - - b.HasIndex("TenantId", "ReportId", "CreatedAt") - .HasDatabaseName("ix_report_status_events_tenant_id_report_id_created_at"); - - b.ToTable("report_status_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CodeCount") - .HasColumnType("integer") - .HasColumnName("code_count"); - - b.Property("CodeEstimated") - .HasColumnType("integer") - .HasColumnName("code_estimated"); - - b.Property("CodeUsed") - .HasColumnType("integer") - .HasColumnName("code_used"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EstimatedRevenueCents") - .HasColumnType("integer") - .HasColumnName("estimated_revenue_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("OrderCount") - .HasColumnType("integer") - .HasColumnName("order_count"); - - b.Property("RealRevenueCents") - .HasColumnType("integer") - .HasColumnName("real_revenue_cents"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_revenue_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_revenue_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_stat_date_region_id_sale_type"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType"), false); - - b.ToTable("revenue_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceAfter") - .HasColumnType("integer") - .HasColumnName("balance_after"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Points") - .HasColumnType("integer") - .HasColumnName("points"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_score_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_score_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_score_events_user_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_user_score_events_tenant_id_idempotency_key"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_user_score_events_tenant_id_user_id_created_at"); - - b.ToTable("user_score_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("LastWrongAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_wrong_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("WrongCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(1) - .HasColumnName("wrong_count"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_wrong_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_wrong_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_wrong_questions_tenant_id_question_id"); - - b.ToTable("wrong_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_announcements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_announcements_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_announcements_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "IsActive", "SortOrder") - .HasDatabaseName("ix_announcements_tenant_id_is_active_sort_order"); - - b.ToTable("announcements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_audit_logs"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_audit_logs_actor_user_id"); - - b.HasIndex("TenantId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_created_at"); - - b.HasIndex("TenantId", "TargetType", "TargetId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_target_type_target_id_created_at"); - - b.ToTable("audit_logs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("ConditionExtra") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("condition_extra") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ConditionField") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("condition_field"); - - b.Property("ConditionOperator") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("condition_operator"); - - b.Property("ConditionValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("condition_value"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon_url"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Level") - .HasColumnType("integer") - .HasColumnName("level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnlockType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("unlock_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_badges_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsActive", "SortOrder") - .HasDatabaseName("ix_badges_tenant_id_category_is_active_sort_order"); - - b.ToTable("badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("BorderColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("border_color"); - - b.Property("ButtonLink") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("button_link"); - - b.Property("ButtonText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("button_text"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Subtitle") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("subtitle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_banners"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_banners_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_banners_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_banners_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("banners", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Answer") - .HasColumnType("text") - .HasColumnName("answer"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Question") - .HasColumnType("text") - .HasColumnName("question"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_faqs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_faqs_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_faqs_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_faqs_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("faqs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("AdoptionId") - .HasColumnType("uuid") - .HasColumnName("adoption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("ReadBy") - .HasColumnType("uuid") - .HasColumnName("read_by"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_content_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_content_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_content_notifications_created_by"); - - b.HasIndex("ReadBy") - .HasDatabaseName("ix_tenant_content_notifications_read_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_source_question_bank~"); - - b.HasIndex("TenantId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_notification_type_de~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "AdoptionId", "NotificationType", "Status") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_adoption_id_notifica~"); - - b.ToTable("tenant_content_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActivePublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ActiveTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("active_template_code"); - - b.Property("ActiveTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DraftPublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("draft_template_code"); - - b.Property("DraftTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftUpdatedBy") - .HasColumnType("uuid") - .HasColumnName("draft_updated_by"); - - b.Property("PublishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("published_at"); - - b.Property("PublishedBy") - .HasColumnType("uuid") - .HasColumnName("published_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_configs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_theme_configs_tenant_id_id"); - - b.HasIndex("ActiveTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_active_template_code"); - - b.HasIndex("DraftTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_draft_template_code"); - - b.HasIndex("DraftUpdatedBy") - .HasDatabaseName("ix_tenant_theme_configs_draft_updated_by"); - - b.HasIndex("PublishedBy") - .HasDatabaseName("ix_tenant_theme_configs_published_by"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_configs_tenant_id"); - - b.ToTable("tenant_theme_configs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PreviewImageUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_image_url"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_templates"); - - b.HasAlternateKey("Code") - .HasName("ak_tenant_theme_templates_code"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_templates_code"); - - b.HasIndex("Status", "SortOrder", "Code") - .HasDatabaseName("ix_tenant_theme_templates_status_sort_order_code"); - - b.ToTable("tenant_theme_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BadgeId") - .HasColumnType("uuid") - .HasColumnName("badge_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrantedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("granted_at"); - - b.Property("GrantedBy") - .HasColumnType("uuid") - .HasColumnName("granted_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_badges_tenant_id_id"); - - b.HasIndex("GrantedBy") - .HasDatabaseName("ix_user_badges_granted_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_badges_user_id"); - - b.HasIndex("TenantId", "BadgeId") - .HasDatabaseName("ix_user_badges_tenant_id_badge_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "BadgeId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_user_id_badge_id"); - - b.ToTable("user_badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_user_notifications_created_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_notifications_user_id"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .HasDatabaseName("ix_user_notifications_tenant_id_source_type_source_id"); - - b.HasIndex("TenantId", "UserId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_notification_type_dedu~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "UserId", "Status", "CreatedAt") - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_status_created_at"); - - b.ToTable("user_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("AcknowledgedBy") - .HasColumnType("uuid") - .HasColumnName("acknowledged_by"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("AuditLogId") - .HasColumnType("uuid") - .HasColumnName("audit_log_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FirstSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("first_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("LastSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("RuleId") - .HasColumnType("uuid") - .HasColumnName("rule_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alerts"); - - b.HasIndex("AcknowledgedBy") - .HasDatabaseName("ix_platform_audit_alerts_acknowledged_by"); - - b.HasIndex("AuditLogId") - .HasDatabaseName("ix_platform_audit_alerts_audit_log_id"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_platform_audit_alerts_resolved_by"); - - b.HasIndex("RuleId", "AuditLogId") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alerts_rule_id_audit_log_id"); - - b.HasIndex("Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_status_severity_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_tenant_id_status_created_at"); - - b.ToTable("platform_audit_alerts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("ActionPatterns") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("action_patterns") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("Conditions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("conditions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.PrimitiveCollection("TargetTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("target_types") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alert_rules"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alert_rules_code"); - - b.HasIndex("TenantId") - .HasDatabaseName("ix_platform_audit_alert_rules_tenant_id"); - - b.HasIndex("Enabled", "Severity", "Code") - .HasDatabaseName("ix_platform_audit_alert_rules_enabled_severity_code"); - - b.ToTable("platform_audit_alert_rules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationChannel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChannelCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel_code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MinReminderLevel") - .HasColumnType("integer") - .HasColumnName("min_reminder_level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.PrimitiveCollection("ReminderChannels") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_channels") - .HasDefaultValueSql("array['internal']::text[]"); - - b.PrimitiveCollection("ReminderTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_types") - .HasDefaultValueSql("array['overdue', 'final_notice']::text[]"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.PrimitiveCollection("TenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WebhookUrl") - .IsRequired() - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("webhook_url"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_channels"); - - b.HasIndex("ChannelCode") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_channels_channel_code"); - - b.HasIndex("Enabled", "MinReminderLevel", "ChannelCode") - .HasDatabaseName("ix_platform_dunning_notification_channels_enabled_min_reminder~"); - - b.ToTable("platform_dunning_notification_channels", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_channels_level", "min_reminder_level between 1 and 20"); - - t.HasCheckConstraint("ck_platform_dunning_channels_timeout", "timeout_seconds between 1 and 60"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("ChannelId") - .HasColumnType("uuid") - .HasColumnName("channel_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.Property("ReminderId") - .HasColumnType("uuid") - .HasColumnName("reminder_id"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScheduledAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at") - .HasDefaultValueSql("now()"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_platform_dunning_notification_events_tenant_id_id"); - - b.HasIndex("ChannelId", "ReminderId") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_events_channel_id_reminder_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_invoice_id"); - - b.HasIndex("TenantId", "ReminderId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_reminder_id"); - - b.HasIndex("InvoiceId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_invoice_id_status_crea~"); - - b.HasIndex("ReminderId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_reminder_id_status_cre~"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_status_creat~"); - - b.HasIndex("Status", "NextAttemptAt", "ScheduledAt", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_status_next_attempt_at~"); - - b.ToTable("platform_dunning_notification_events", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_events_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformSaasPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseAmountCents") - .HasColumnType("integer") - .HasColumnName("base_amount_cents"); - - b.Property("BillingCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_cycle"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IncludedQuotas") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("included_quotas") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OveragePrices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("overage_prices") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_saas_plans"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_saas_plans_code"); - - b.HasIndex("Status", "SortOrder") - .HasDatabaseName("ix_platform_saas_plans_status_sort_order"); - - b.ToTable("platform_saas_plans", null, t => - { - t.HasCheckConstraint("ck_platform_saas_plans_amount", "base_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BankAccountMasked") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("bank_account_masked"); - - b.Property("BankName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bank_name"); - - b.Property("BillingAddress") - .HasColumnType("text") - .HasColumnName("billing_address"); - - b.Property("BillingName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("billing_name"); - - b.Property("ContactEmail") - .HasMaxLength(300) - .HasColumnType("citext") - .HasColumnName("contact_email"); - - b.Property("ContactName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("contact_name"); - - b.Property("ContactPhone") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("contact_phone"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceTitle") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("invoice_title"); - - b.Property("InvoiceType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TaxId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("tax_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_billing_profiles"); - - b.ToTable("tenant_billing_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceCents") - .HasColumnType("integer") - .HasColumnName("balance_cents"); - - b.Property("BillingPeriodEnd") - .HasColumnType("date") - .HasColumnName("billing_period_end"); - - b.Property("BillingPeriodStart") - .HasColumnType("date") - .HasColumnName("billing_period_start"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("DiscountCents") - .HasColumnType("integer") - .HasColumnName("discount_cents"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("invoice_no"); - - b.Property("InvoiceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidCents") - .HasColumnType("integer") - .HasColumnName("paid_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubtotalCents") - .HasColumnType("integer") - .HasColumnName("subtotal_cents"); - - b.Property("TaxCents") - .HasColumnType("integer") - .HasColumnName("tax_cents"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCents") - .HasColumnType("integer") - .HasColumnName("total_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoices_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoices_created_by"); - - b.HasIndex("InvoiceNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_invoice_no"); - - b.HasIndex("TenantId", "BillingPeriodStart", "BillingPeriodEnd") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_tenant_id_billing_period_start_billing_peri~") - .HasFilter("invoice_type = 'usage_overage' and status <> 'void' and metadata->>'source' = 'usage_overage_auto'"); - - b.HasIndex("TenantId", "Status", "DueDate") - .HasDatabaseName("ix_tenant_invoices_tenant_id_status_due_date"); - - b.ToTable("tenant_invoices", null, t => - { - t.HasCheckConstraint("ck_tenant_invoices_amounts", "subtotal_cents >= 0 and discount_cents >= 0 and tax_cents >= 0 and total_cents >= 0 and paid_cents >= 0 and balance_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoices_period", "billing_period_end is null or billing_period_start is null or billing_period_end >= billing_period_start"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("description"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("ItemRefId") - .HasColumnType("uuid") - .HasColumnName("item_ref_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Quantity") - .HasPrecision(12, 2) - .HasColumnType("numeric(12,2)") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_items_tenant_id_id"); - - b.HasIndex("InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_invoice_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_items", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_items_amounts", "unit_amount_cents >= 0 and amount_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_no"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReceivedBy") - .HasColumnType("uuid") - .HasColumnName("received_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_payments_tenant_id_id"); - - b.HasIndex("PaymentNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_payments_payment_no"); - - b.HasIndex("ReceivedBy") - .HasDatabaseName("ix_tenant_invoice_payments_received_by"); - - b.HasIndex("InvoiceId", "Status") - .HasDatabaseName("ix_tenant_invoice_payments_invoice_id_status"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_payments_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_payments", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("BalanceCentsSnapshot") - .HasColumnType("integer") - .HasColumnName("balance_cents_snapshot"); - - b.Property("Channel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("channel"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Message") - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReminderDate") - .HasColumnType("date") - .HasColumnName("reminder_date"); - - b.Property("ReminderLevel") - .HasColumnType("integer") - .HasColumnName("reminder_level"); - - b.Property("ReminderType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("reminder_type"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_reminders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_reminders_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoice_reminders_created_by"); - - b.HasIndex("InvoiceId", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_invoice_id_reminder_date"); - - b.HasIndex("TenantId", "Status", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_status_reminder_date"); - - b.HasIndex("TenantId", "InvoiceId", "ReminderType", "Channel", "ReminderDate") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_invoice_id_reminder_type~"); - - b.ToTable("tenant_invoice_reminders", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_reminders_balance", "balance_cents_snapshot >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_reminders_level", "reminder_level between 1 and 20"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CurrentVersionId") - .HasColumnType("uuid") - .HasColumnName("current_version_id"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExamMarkers") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("exam_markers") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("HasVideoExplanation") - .HasColumnType("boolean") - .HasColumnName("has_video_explanation"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyNodeId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_node_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("MediaUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("media_url"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PrimaryCollectionId") - .HasColumnType("uuid") - .HasColumnName("primary_collection_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("TypeLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("type_label"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_questions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_questions_tenant_id_id"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_questions_tenant_id_category_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_questions_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_questions_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_questions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_questions_tenant_id_node_id"); - - b.HasIndex("TenantId", "PrimaryCollectionId") - .HasDatabaseName("ix_questions_tenant_id_primary_collection_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_questions_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_questions_tenant_id_subject_id"); - - b.HasIndex("TenantId", "Id", "CurrentVersionId") - .HasDatabaseName("ix_questions_tenant_id_id_current_version_id"); - - b.ToTable("questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SourceScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_scope"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_banks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_banks_tenant_id_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_banks_tenant_id_region_id"); - - b.ToTable("question_banks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("CodeLang") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code_lang"); - - b.Property("CodeTemplate") - .HasColumnType("text") - .HasColumnName("code_template"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CorrectOptionIndex") - .HasColumnType("integer") - .HasColumnName("correct_option_index"); - - b.Property("CorrectOptionIndices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("correct_option_indices") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Explanation") - .HasColumnType("text") - .HasColumnName("explanation"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubQuestions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sub_questions") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("VersionNo") - .HasColumnType("integer") - .HasColumnName("version_no"); - - b.HasKey("Id") - .HasName("pk_question_versions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_versions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "QuestionId", "Id") - .HasName("ak_question_versions_tenant_id_question_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_versions_created_by"); - - b.HasIndex("QuestionId", "VersionNo") - .IsUnique() - .HasDatabaseName("ix_question_versions_question_id_version_no"); - - b.ToTable("question_versions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FailureCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("failure_code"); - - b.Property("Identifier") - .HasMaxLength(320) - .HasColumnType("character varying(320)") - .HasColumnName("identifier"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_login_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_login_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_login_events_user_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_auth_login_events_tenant_id_user_id_created_at"); - - b.ToTable("auth_login_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TokenHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("token_hash"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_sessions_tenant_id_id"); - - b.HasIndex("TokenHash") - .IsUnique() - .HasDatabaseName("ix_auth_sessions_token_hash"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_sessions_user_id"); - - b.HasIndex("TenantId", "UserId", "ExpiresAt") - .HasDatabaseName("ix_auth_sessions_tenant_id_user_id_expires_at") - .HasFilter("revoked_at is null"); - - b.ToTable("auth_sessions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Dimension") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("dimension"); - - b.Property("ScopeHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("scope_hash"); - - b.Property("BucketStart") - .HasColumnType("timestamp with time zone") - .HasColumnName("bucket_start"); - - b.Property("RequestCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(0) - .HasColumnName("request_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId", "Dimension", "ScopeHash", "BucketStart") - .HasName("pk_sms_send_rate_limits"); - - b.HasIndex("UpdatedAt") - .HasDatabaseName("ix_sms_send_rate_limits_updated_at"); - - b.ToTable("sms_send_rate_limits", null, t => - { - t.HasCheckConstraint("ck_sms_send_rate_limits_request_count", "request_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CodeHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("code_hash"); - - b.Property("ConsumedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("consumed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phone") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Purpose") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("purpose"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_sms_verification_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_sms_verification_codes_tenant_id_id"); - - b.HasIndex("TenantId", "Phone", "Purpose") - .IsUnique() - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "CreatedAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_created_at") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "ExpiresAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_expires_at"); - - b.ToTable("sms_verification_codes", null, t => - { - t.HasCheckConstraint("ck_sms_verification_codes_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BillingStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_status"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegalName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("legal_name"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OwnerUserId") - .HasColumnType("uuid") - .HasColumnName("owner_user_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("slug"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenants"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenants_legacy_id"); - - b.HasIndex("OwnerUserId") - .HasDatabaseName("ix_tenants_owner_user_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_tenants_slug"); - - b.ToTable("tenants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_auth_providers"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_auth_providers_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider"); - - b.HasIndex("TenantId", "Provider", "Status") - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider_status"); - - b.ToTable("tenant_auth_providers", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BrandName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("brand_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FaviconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("favicon_url"); - - b.Property("LogoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("logo_url"); - - b.Property("OrganizationName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("organization_name"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ServiceAccountName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("service_account_name"); - - b.Property("ServiceWechat") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("service_wechat"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("Slogan") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("slogan"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_branding"); - - b.ToTable("tenant_branding", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_classes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_classes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_classes_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_classes_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_code") - .HasFilter("code is not null and code <> ''"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_tenant_classes_tenant_id_region_id") - .HasFilter("region_id is not null"); - - b.HasIndex("TenantId", "Status", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_tenant_classes_tenant_id_status_sort_order_created_at"); - - b.ToTable("tenant_classes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("JoinedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("joined_at") - .HasDefaultValueSql("now()"); - - b.Property("LeftAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("left_at"); - - b.Property("MemberType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("member_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_class_members"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_class_members_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_class_members_user_id"); - - b.HasIndex("TenantId", "ClassId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_status_member_type"); - - b.HasIndex("TenantId", "ClassId", "UserId", "MemberType") - .IsUnique() - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_user_id_member_type"); - - b.HasIndex("TenantId", "UserId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_user_id_status_member_type"); - - b.ToTable("tenant_class_members", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DomainType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("domain_type"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(253) - .HasColumnType("citext") - .HasColumnName("host"); - - b.Property("IsPrimary") - .HasColumnType("boolean") - .HasColumnName("is_primary"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VerificationToken") - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("verification_token"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.HasKey("Id") - .HasName("pk_tenant_domains"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_domains_tenant_id_id"); - - b.HasIndex("Host") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_host"); - - b.HasIndex("TenantId", "IsPrimary") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_tenant_id_is_primary") - .HasFilter("is_primary"); - - b.ToTable("tenant_domains", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyRole") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("legacy_role"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Role") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("role"); - - b.Property("RoleTemplateId") - .HasColumnType("uuid") - .HasColumnName("role_template_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_memberships"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_memberships_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_memberships_user_id"); - - b.HasIndex("TenantId", "RoleTemplateId") - .HasDatabaseName("ix_tenant_memberships_tenant_id_role_template_id"); - - b.HasIndex("TenantId", "UserId", "Role") - .IsUnique() - .HasDatabaseName("ix_tenant_memberships_tenant_id_user_id_role"); - - b.ToTable("tenant_memberships", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("base_role"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DataScope") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("data_scope") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FieldPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IsSystem") - .HasColumnType("boolean") - .HasColumnName("is_system"); - - b.Property("MenuPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("menu_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModulePermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_role_templates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_role_templates_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_role_templates_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_role_templates_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_role_templates_tenant_id_code"); - - b.HasIndex("TenantId", "Status", "SortOrder") - .HasDatabaseName("ix_tenant_role_templates_tenant_id_status_sort_order"); - - b.ToTable("tenant_role_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("AdminFeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("admin_feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PublicConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_settings"); - - b.ToTable("tenant_settings", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CompletedBy") - .HasColumnType("uuid") - .HasColumnName("completed_by"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("FollowupType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("followup_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_student_followups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_followups_tenant_id_id"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_tenant_student_followups_assigned_to_user_id"); - - b.HasIndex("CompletedBy") - .HasDatabaseName("ix_tenant_student_followups_completed_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_followups_student_user_id"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_assigned_to_user_id_stat~") - .HasFilter("assigned_to_user_id is not null"); - - b.HasIndex("TenantId", "ClassId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_class_id_status_due_at") - .HasFilter("class_id is not null"); - - b.HasIndex("TenantId", "StudentUserId", "Status", "DueAt", "CreatedAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_student_user_id_status_d~"); - - b.ToTable("tenant_student_followups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("IsPinned") - .HasColumnType("boolean") - .HasColumnName("is_pinned"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NoteType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("note_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_tenant_student_notes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_notes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_student_notes_created_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_notes_student_user_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_student_notes_updated_by"); - - b.HasIndex("TenantId", "CreatedBy", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_created_by_created_at") - .HasFilter("created_by is not null"); - - b.HasIndex("TenantId", "StudentUserId", "IsPinned", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_student_user_id_is_pinned_cr~"); - - b.ToTable("tenant_student_notes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_categories_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_majors_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_module_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_module_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_regions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_region_modules_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_region_modules_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_schools_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineField", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_scoreline_fields_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_fields_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_scoreline_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AgentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_agent_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_activation_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UsedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_used_by"); - - b.HasOne("Tiku.Domain.Commerce.CodeBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_code_batches_tenant_id_batch_id"); - - b.HasOne("Tiku.Domain.Commerce.CouponRedemption", null) - .WithMany() - .HasForeignKey("TenantId", "CouponRedemptionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_coupon_redemptions_tenant_id_coupon_redemp~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "UsedRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_regions_tenant_id_used_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_code_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_batches_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedTo") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_assigned_to"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_resolved_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_batc~"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_item~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_refund_requests_ten~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issue_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationIssue", null) - .WithMany() - .HasForeignKey("TenantId", "IssueId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_commerce_reconciliatio~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_commerce_reconciliation_batch~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_commerce_refund_requests_tena~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_commerce_refund_requests_tenant_id_r~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ProcessedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_processed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RequestedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_requested_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_refund_requests_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupons_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_coupons_svip_plans_tenant_id_plan_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupon_redemptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.Coupon", null) - .WithMany() - .HasForeignKey("TenantId", "CouponId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_coupons_tenant_id_coupon_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RevokedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_entitlements_users_revoked_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_orders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_orders_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payment_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_payment_events_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_products_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_products_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_svip_plans_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_svip_plans_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_payment_accounts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_subscriptions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_usage_records_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_ai_recommendation_reports_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_app_assets_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_assets_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("VerifiedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_verified_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_content_nodes_tenant_id_content_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_asset_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_access_events_content_assets_tenant_id_asset_~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_security_scan_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_security_scan_events_content_assets_tenant_id~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_entries_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_import_issues_content_import_items_tenant_id_item_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_import_jobs_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_jobs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_categories_tenant_id_target_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "TargetContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_content_nodes_tenant_id_target_content_~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_question_banks_tenant_id_target_questio~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "TargetRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_regions_tenant_id_target_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_nodes_content_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_chapters_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_chapters_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_handbook_chapters_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.HandbookSubject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_chapters_handbook_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.HandbookChapter", null) - .WithMany() - .HasForeignKey("TenantId", "ChapterId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_entries_handbook_chapters_tenant_id_chapter_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_entries_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_handbook_entries_content_entries_tenant_id_entry_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_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_handbook_subjects_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_images_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_blueprints_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_question_collections_tenant_id_collecti~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_bank_grants_question_banks_source_question_bank_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_question_banks_tenant_id_question_bank~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_question_collections_tenant_id_co~"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_type_groups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_type_groups_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_videos_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.Content.VideoExplanation", null) - .WithMany() - .HasForeignKey("TenantId", "VideoId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_video_explanations_tenant_id_video_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SourceSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_source_subject_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_created_by"); - - b.HasOne("Tiku.Domain.Content.QuestionBankGrant", null) - .WithMany() - .HasForeignKey("GrantId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_bank_grants_grant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_source_questi~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_collections_tenant_~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "TargetEntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_content_entries_tenant_id_ta~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_tenant_id_tar~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_video_explanations_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_video_explanations_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_units_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_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_vocabulary_units_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_words_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_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_vocabulary_words_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyUnit", null) - .WithMany() - .HasForeignKey("TenantId", "UnitId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_vocabulary_units_tenant_id_unit_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GeneratedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_generated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PaidBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_paid_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.CommissionSettlement", null) - .WithMany() - .HasForeignKey("TenantId", "SettlementId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_commission_settlements_tenant_i~"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_config_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_log_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_queue_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralTrack", null) - .WithMany() - .HasForeignKey("TenantId", "FirstTrackId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_referral_tracks_tenant_id_first_track_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_qrcodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_qrcodes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("LeaderUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_team_edges_users_leader_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("MemberUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_users_member_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("TargetUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_target_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_tracks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralLead", null) - .WithMany() - .HasForeignKey("TenantId", "LeadId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_referral_leads_tenant_id_lead_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_commission_settings_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_commission_settings_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedMajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_majors_tenant_id_selected_major_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedSchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_schools_tenant_id_selected_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_identities_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_pb_import_issues_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_runs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId", "QuestionVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_question_versions_tenant_id_question_id_ques~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "UserId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "UserId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_practice_sessions_tenant_id_user_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_dashboard_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_dashboard_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_exam_dates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_practice_sessions_tenant_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .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.PracticeSessionReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .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") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_practice_sessions_tenant_id_practi~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_sessions_tenant_i~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSessionReport", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_session_reports_t~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_recent_practices_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_recent_practices_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("HandledBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_handled_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_reports_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_report_status_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.Report", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_reports_tenant_id_report_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_revenue_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_revenue_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_announcements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_audit_logs_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_audit_logs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_badges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_banners_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_banners_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_faqs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_faqs_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReadBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_read_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_content_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "SourceQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_tenant_content_notifications_question_banks_tenant_id_sourc~"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("ActiveTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_active_template~"); - - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("DraftTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_draft_template_~"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("DraftUpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_draft_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PublishedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_published_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_theme_configs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GrantedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_badges_users_granted_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_badges_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_users_user_id"); - - b.HasOne("Tiku.Domain.Operations.Badge", null) - .WithMany() - .HasForeignKey("TenantId", "BadgeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_badges_tenant_id_badge_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AcknowledgedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_acknowledged_by"); - - b.HasOne("Tiku.Domain.Operations.AuditLog", null) - .WithMany() - .HasForeignKey("AuditLogId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_audit_logs_audit_log_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_resolved_by"); - - b.HasOne("Tiku.Domain.Platform.PlatformAuditAlertRule", null) - .WithMany() - .HasForeignKey("RuleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_platform_audit_alert_rules_rule_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alerts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alert_rules_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.HasOne("Tiku.Domain.Platform.PlatformDunningNotificationChannel", null) - .WithMany() - .HasForeignKey("ChannelId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_platform_dunning_notif~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoices_tenant~"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoiceReminder", null) - .WithMany() - .HasForeignKey("TenantId", "ReminderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoice_reminde~"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Platform.TenantBillingProfile", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_billing_profiles_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoices_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoices_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReceivedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_payments_users_received_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_reminders_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenant_invoices_tenant_id_invoice_~"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_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_questions_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "PrimaryCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_collections_tenant_id_primary_collection~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_banks_tenant_id_question_bank_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_subjects_tenant_id_subject_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "Id", "CurrentVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_versions_tenant_id_id_current_version_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_banks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_banks_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_versions_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_versions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_login_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_auth_login_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_send_rate_limits_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_verification_codes_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("OwnerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenants_users_owner_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_auth_providers_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantBranding", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_branding_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_classes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_classes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_domains_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantRoleTemplate", null) - .WithMany() - .HasForeignKey("TenantId", "RoleTemplateId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_role_templates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantSettings", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_settings_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CompletedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_completed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_updated_by"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726102813_AddScorelineDynamicRecords.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726102813_AddScorelineDynamicRecords.cs deleted file mode 100644 index ebb7382..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726102813_AddScorelineDynamicRecords.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System; -using System.Text.Json; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - /// - public partial class AddScorelineDynamicRecords : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "scoreline_fields", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - region_id = table.Column(type: "uuid", nullable: true), - legacy_id = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), - field_key = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), - field_name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), - field_type = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - unit = table.Column(type: "character varying(32)", maxLength: 32, nullable: true), - is_filter = table.Column(type: "boolean", nullable: false), - is_required = table.Column(type: "boolean", nullable: false), - is_visible = table.Column(type: "boolean", nullable: false), - is_trend = table.Column(type: "boolean", nullable: false), - options = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"), - placeholder = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), - description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), - sort_order = table.Column(type: "integer", nullable: false), - tenant_id = table.Column(type: "uuid", nullable: false), - created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") - }, - constraints: table => - { - table.PrimaryKey("pk_scoreline_fields", x => x.id); - table.UniqueConstraint("ak_scoreline_fields_tenant_id_id", x => new { x.tenant_id, x.id }); - table.ForeignKey( - name: "fk_scoreline_fields_regions_tenant_id_region_id", - columns: x => new { x.tenant_id, x.region_id }, - principalTable: "regions", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_scoreline_fields_tenants_tenant_id", - column: x => x.tenant_id, - principalTable: "tenants", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "scoreline_records", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - region_id = table.Column(type: "uuid", nullable: true), - school_id = table.Column(type: "uuid", nullable: true), - major_id = table.Column(type: "uuid", nullable: true), - legacy_id = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), - year = table.Column(type: "integer", nullable: false), - school_name = table.Column(type: "character varying(300)", maxLength: 300, nullable: true), - major_name = table.Column(type: "character varying(300)", maxLength: 300, nullable: true), - field_values = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - tenant_id = table.Column(type: "uuid", nullable: false), - created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") - }, - constraints: table => - { - table.PrimaryKey("pk_scoreline_records", x => x.id); - table.UniqueConstraint("ak_scoreline_records_tenant_id_id", x => new { x.tenant_id, x.id }); - table.ForeignKey( - name: "fk_scoreline_records_majors_tenant_id_major_id", - columns: x => new { x.tenant_id, x.major_id }, - principalTable: "majors", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_scoreline_records_regions_tenant_id_region_id", - columns: x => new { x.tenant_id, x.region_id }, - principalTable: "regions", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_scoreline_records_schools_tenant_id_school_id", - columns: x => new { x.tenant_id, x.school_id }, - principalTable: "schools", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_scoreline_records_tenants_tenant_id", - column: x => x.tenant_id, - principalTable: "tenants", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "ix_scoreline_fields_tenant_id_legacy_id", - table: "scoreline_fields", - columns: new[] { "tenant_id", "legacy_id" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "ix_scoreline_fields_tenant_id_region_id_field_key", - table: "scoreline_fields", - columns: new[] { "tenant_id", "region_id", "field_key" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "ix_scoreline_fields_tenant_id_region_id_is_filter_sort_order", - table: "scoreline_fields", - columns: new[] { "tenant_id", "region_id", "is_filter", "sort_order" }); - - migrationBuilder.CreateIndex( - name: "ix_scoreline_records_tenant_id_legacy_id", - table: "scoreline_records", - columns: new[] { "tenant_id", "legacy_id" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "ix_scoreline_records_tenant_id_major_id", - table: "scoreline_records", - columns: new[] { "tenant_id", "major_id" }); - - migrationBuilder.CreateIndex( - name: "ix_scoreline_records_tenant_id_region_id_school_id_major_id_ye~", - table: "scoreline_records", - columns: new[] { "tenant_id", "region_id", "school_id", "major_id", "year" }); - - migrationBuilder.CreateIndex( - name: "ix_scoreline_records_tenant_id_school_id", - table: "scoreline_records", - columns: new[] { "tenant_id", "school_id" }); - - migrationBuilder.CreateIndex( - name: "ix_scoreline_records_tenant_id_year", - table: "scoreline_records", - columns: new[] { "tenant_id", "year" }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "scoreline_fields"); - - migrationBuilder.DropTable( - name: "scoreline_records"); - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726103534_AddStudentProfileAvatarPreset.Designer.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726103534_AddStudentProfileAvatarPreset.Designer.cs deleted file mode 100644 index c75f627..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726103534_AddStudentProfileAvatarPreset.Designer.cs +++ /dev/null @@ -1,15562 +0,0 @@ -// -using System; -using System.Text.Json; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using Tiku.Infrastructure.Persistence; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - [DbContext(typeof(TikuDbContext))] - [Migration("20260726103534_AddStudentProfileAvatarPreset")] - partial class AddStudentProfileAvatarPreset - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext"); - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "ltree"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("category_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("SvipQuestionLimit") - .HasColumnType("integer") - .HasColumnName("svip_question_limit"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_categories"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_categories_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_categories_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_categories_tenant_id_node_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_categories_tenant_id_subject_id"); - - b.ToTable("categories", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("StudyTips") - .HasColumnType("text") - .HasColumnName("study_tips"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_majors"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_majors_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_majors_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_majors_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_majors_tenant_id_school_id"); - - b.ToTable("majors", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyModuleId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_module_id"); - - b.Property("LegacyParentId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_parent_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_module_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_module_nodes_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_module_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_module_nodes_tenant_id_module_id"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_module_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_module_nodes_tenant_id_region_id"); - - b.ToTable("module_nodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FullName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("full_name"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsHot") - .HasColumnType("boolean") - .HasColumnName("is_hot"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Pinyin") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("pinyin"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_regions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_regions_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_regions_tenant_id_legacy_id"); - - b.ToTable("regions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsPrimarySchoolModule") - .HasColumnType("boolean") - .HasColumnName("is_primary_school_module"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TextColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("text_color"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_region_modules"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_region_modules_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_region_modules_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_region_modules_tenant_id_region_id"); - - b.ToTable("region_modules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ProfessionalExamDate") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("professional_exam_date"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_schools"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_schools_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_schools_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_schools_tenant_id_module_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_schools_tenant_id_region_id"); - - b.ToTable("schools", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineField", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FieldKey") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("field_key"); - - b.Property("FieldName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("field_name"); - - b.Property("FieldType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("field_type"); - - b.Property("IsFilter") - .HasColumnType("boolean") - .HasColumnName("is_filter"); - - b.Property("IsRequired") - .HasColumnType("boolean") - .HasColumnName("is_required"); - - b.Property("IsTrend") - .HasColumnType("boolean") - .HasColumnName("is_trend"); - - b.Property("IsVisible") - .HasColumnType("boolean") - .HasColumnName("is_visible"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Placeholder") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("placeholder"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Unit") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("unit"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_scoreline_fields"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_scoreline_fields_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_scoreline_fields_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "FieldKey") - .IsUnique() - .HasDatabaseName("ix_scoreline_fields_tenant_id_region_id_field_key"); - - b.HasIndex("TenantId", "RegionId", "IsFilter", "SortOrder") - .HasDatabaseName("ix_scoreline_fields_tenant_id_region_id_is_filter_sort_order"); - - b.ToTable("scoreline_fields", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldValues") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_values") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("major_name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SchoolName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("school_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Year") - .HasColumnType("integer") - .HasColumnName("year"); - - b.HasKey("Id") - .HasName("pk_scoreline_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_scoreline_records_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_scoreline_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_scoreline_records_tenant_id_major_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_scoreline_records_tenant_id_school_id"); - - b.HasIndex("TenantId", "Year") - .HasDatabaseName("ix_scoreline_records_tenant_id_year"); - - b.HasIndex("TenantId", "RegionId", "SchoolId", "MajorId", "Year") - .HasDatabaseName("ix_scoreline_records_tenant_id_region_id_school_id_major_id_ye~"); - - b.ToTable("scoreline_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_subjects_tenant_id_module_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_subjects_tenant_id_node_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_subjects_tenant_id_school_id"); - - b.ToTable("subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AgentUserId") - .HasColumnType("uuid") - .HasColumnName("agent_user_id"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponRedemptionId") - .HasColumnType("uuid") - .HasColumnName("coupon_redemption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("IsUsed") - .HasColumnType("boolean") - .HasColumnName("is_used"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("SoldTo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("sold_to"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitPriceCents") - .HasColumnType("integer") - .HasColumnName("unit_price_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UsedBy") - .HasColumnType("uuid") - .HasColumnName("used_by"); - - b.Property("UsedRegionId") - .HasColumnType("uuid") - .HasColumnName("used_region_id"); - - b.HasKey("Id") - .HasName("pk_activation_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_activation_codes_tenant_id_id"); - - b.HasIndex("AgentUserId") - .HasDatabaseName("ix_activation_codes_agent_user_id"); - - b.HasIndex("UsedBy") - .HasDatabaseName("ix_activation_codes_used_by"); - - b.HasIndex("TenantId", "BatchId") - .HasDatabaseName("ix_activation_codes_tenant_id_batch_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_code"); - - b.HasIndex("TenantId", "CouponRedemptionId") - .HasDatabaseName("ix_activation_codes_tenant_id_coupon_redemption_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UsedRegionId") - .HasDatabaseName("ix_activation_codes_tenant_id_used_region_id"); - - b.ToTable("activation_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CampaignName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("campaign_name"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CostPriceCents") - .HasColumnType("integer") - .HasColumnName("cost_price_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("DefaultUnitPriceCents") - .HasColumnType("integer") - .HasColumnName("default_unit_price_cents"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_code_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_code_batches_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_code_batches_tenant_id_legacy_id"); - - b.ToTable("code_batches", null, t => - { - t.HasCheckConstraint("ck_code_batches_amounts", "default_unit_price_cents >= 0 and cost_price_cents >= 0"); - - t.HasCheckConstraint("ck_code_batches_counts", "total_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillDate") - .HasColumnType("date") - .HasColumnName("bill_date"); - - b.Property("BillType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bill_type"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DuplicateCount") - .HasColumnType("integer") - .HasColumnName("duplicate_count"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IgnoredCount") - .HasColumnType("integer") - .HasColumnName("ignored_count"); - - b.Property("MatchedCount") - .HasColumnType("integer") - .HasColumnName("matched_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MismatchCount") - .HasColumnType("integer") - .HasColumnName("mismatch_count"); - - b.Property("MissingLocalCount") - .HasColumnType("integer") - .HasColumnName("missing_local_count"); - - b.Property("MissingProviderCount") - .HasColumnType("integer") - .HasColumnName("missing_provider_count"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source"); - - b.Property("SourceHash") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("source_name"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_batches_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_batches_created_by"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "Provider", "BillDate", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_provider_bill_dat~"); - - b.ToTable("commerce_reconciliation_batches", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_batches_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_batches_counts", "total_count >= 0 and matched_count >= 0 and mismatch_count >= 0 and missing_local_count >= 0 and missing_provider_count >= 0 and duplicate_count >= 0 and ignored_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("AssignedTo") - .HasColumnType("uuid") - .HasColumnName("assigned_to"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("IssueNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_no"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolutionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("resolution_type"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("summary"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issues_tenant_id_id"); - - b.HasIndex("AssignedTo") - .HasDatabaseName("ix_commerce_reconciliation_issues_assigned_to"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_created_by"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_resolved_by"); - - b.HasIndex("TenantId", "IssueNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_issue_no"); - - b.HasIndex("TenantId", "ItemId") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_item_id") - .HasFilter("item_id is not null and status in ('open', 'investigating', 'escalated')"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "PaymentId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_payment_id_created~"); - - b.HasIndex("TenantId", "AssignedTo", "Status", "DueAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_assigned_to_status~"); - - b.HasIndex("TenantId", "BatchId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_batch_id_status_cr~"); - - b.HasIndex("TenantId", "Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_status_severity_cr~"); - - b.ToTable("commerce_reconciliation_issues", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_issues_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("IssueId") - .HasColumnType("uuid") - .HasColumnName("issue_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issue_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issue_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_actor_user_id"); - - b.HasIndex("TenantId", "IssueId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_tenant_id_issue_id_cre~"); - - b.ToTable("commerce_reconciliation_issue_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LocalStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("local_status"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider_status"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("RefundedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("refunded_at"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_items_tenant_id_id"); - - b.HasIndex("BatchId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_items_batch_id_row_no"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "BatchId", "MatchStatus", "RowNo") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_batch_id_match_stat~"); - - b.HasIndex("TenantId", "Provider", "ProviderTradeNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_provider_provider_t~"); - - b.HasIndex("TenantId", "RefundNo", "ProviderRefundNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_no_provider_~"); - - b.ToTable("commerce_reconciliation_items", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_items_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_items_row", "row_no > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_refund_events_actor_user_id"); - - b.HasIndex("TenantId", "RefundRequestId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_events_tenant_id_refund_request_id_created_~"); - - b.ToTable("commerce_refund_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CancelledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("cancelled_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementAction") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entitlement_action"); - - b.Property("FailedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("failed_at"); - - b.Property("FailureReason") - .HasColumnType("text") - .HasColumnName("failure_reason"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("ProcessedBy") - .HasColumnType("uuid") - .HasColumnName("processed_by"); - - b.Property("Provider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("Reason") - .HasColumnType("text") - .HasColumnName("reason"); - - b.Property("RefundNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RequestedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("requested_at") - .HasDefaultValueSql("now()"); - - b.Property("RequestedBy") - .HasColumnType("uuid") - .HasColumnName("requested_by"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SucceededAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("succeeded_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_requests"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_requests_tenant_id_id"); - - b.HasIndex("ProcessedBy") - .HasDatabaseName("ix_commerce_refund_requests_processed_by"); - - b.HasIndex("RequestedBy") - .HasDatabaseName("ix_commerce_refund_requests_requested_by"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commerce_refund_requests_reviewed_by"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundNo") - .IsUnique() - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_refund_no"); - - b.HasIndex("TenantId", "OrderId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_order_id_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_status_created_at"); - - b.ToTable("commerce_refund_requests", null, t => - { - t.HasCheckConstraint("ck_commerce_refund_requests_amount", "amount_cents > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("discount_type"); - - b.Property("DiscountValue") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("discount_value"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MaxUses") - .HasColumnType("integer") - .HasColumnName("max_uses"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("ValidFrom") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_from"); - - b.Property("ValidTo") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_to"); - - b.HasKey("Id") - .HasName("pk_coupons"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupons_tenant_id_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_code"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupons_tenant_id_plan_id"); - - b.ToTable("coupons", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClaimedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("claimed_at"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponId") - .HasColumnType("uuid") - .HasColumnName("coupon_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountAppliedCents") - .HasColumnType("integer") - .HasColumnName("discount_applied_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_coupon_redemptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupon_redemptions_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_coupon_redemptions_user_id"); - - b.HasIndex("TenantId", "CouponId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_coupon_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_region_id"); - - b.HasIndex("TenantId", "OrderId", "UserId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_order_id_user_id") - .HasFilter("order_id is not null"); - - b.HasIndex("TenantId", "UserId", "CouponId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_user_id_coupon_id") - .HasFilter("coupon_id is not null"); - - b.ToTable("coupon_redemptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("entitlement_type"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("LegacySourceId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_source_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("RevokedBy") - .HasColumnType("uuid") - .HasColumnName("revoked_by"); - - b.Property("RevokedReason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("revoked_reason"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("StartsAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at") - .HasDefaultValueSql("now()"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_entitlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_entitlements_tenant_id_id"); - - b.HasIndex("RevokedBy") - .HasDatabaseName("ix_entitlements_revoked_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_entitlements_user_id"); - - b.HasIndex("TenantId", "UserId", "Status", "ExpiresAt") - .HasDatabaseName("ix_entitlements_tenant_id_user_id_status_expires_at"); - - b.ToTable("entitlements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPlanId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_plan_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("OrderNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PayMethod") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_method"); - - b.Property("PayProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_provider"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("ProductName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("product_name"); - - b.Property("ProductType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("product_type"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("trade_no"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_orders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_orders_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_orders_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderNo") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_order_no"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_orders_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_orders_tenant_id_region_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_orders_tenant_id_user_id_created_at"); - - b.ToTable("orders", null, t => - { - t.HasCheckConstraint("ck_orders_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_orders_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("Quantity") - .HasColumnType("integer") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalAmountCents") - .HasColumnType("integer") - .HasColumnName("total_amount_cents"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_order_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_order_items_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_order_items_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_order_items_tenant_id_order_id"); - - b.ToTable("order_items", null, t => - { - t.HasCheckConstraint("ck_order_items_amounts", "unit_amount_cents >= 0 and total_amount_cents >= 0"); - - t.HasCheckConstraint("ck_order_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyOrderId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_order_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payments_tenant_id_id"); - - b.HasIndex("Provider", "ProviderTradeNo") - .IsUnique() - .HasDatabaseName("ix_payments_provider_provider_trade_no") - .HasFilter("provider_trade_no is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_payments_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId", "UpdatedAt") - .HasDatabaseName("ix_payments_tenant_id_order_id_updated_at"); - - b.ToTable("payments", null, t => - { - t.HasCheckConstraint("ck_payments_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_payments_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("EventId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("event_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("SignatureValid") - .HasColumnType("boolean") - .HasColumnName("signature_valid"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_payment_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payment_events_tenant_id_id"); - - b.HasIndex("Provider", "EventId") - .IsUnique() - .HasDatabaseName("ix_payment_events_provider_event_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_payment_events_tenant_id_payment_id"); - - b.ToTable("payment_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Cover") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cover"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DetailImages") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("detail_images") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true) - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPriceText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_price_text"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("PreviewIframe") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_iframe"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_products"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_products_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_products_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "Type", "IsActive", "SortOrder") - .HasDatabaseName("ix_products_tenant_id_region_id_type_is_active_sort_order"); - - b.ToTable("products", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Badge") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("badge"); - - b.Property("CouponOnly") - .HasColumnType("boolean") - .HasColumnName("coupon_only"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OriginalPriceCents") - .HasColumnType("integer") - .HasColumnName("original_price_cents"); - - b.Property("PerDayLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("per_day_label"); - - b.Property("PriceCents") - .HasColumnType("integer") - .HasColumnName("price_cents"); - - b.Property("Recommended") - .HasColumnType("boolean") - .HasColumnName("recommended"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VpEnabled") - .HasColumnType("boolean") - .HasColumnName("vp_enabled"); - - b.Property("VpProductId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("vp_product_id"); - - b.HasKey("Id") - .HasName("pk_svip_plans"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_svip_plans_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_svip_plans_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_svip_plans_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("svip_plans", null, t => - { - t.HasCheckConstraint("ck_svip_plans_days", "days >= 0"); - - t.HasCheckConstraint("ck_svip_plans_price", "price_cents >= 0 and (original_price_cents is null or original_price_cents >= 0)"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_payment_accounts"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_payment_accounts_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_payment_accounts_tenant_id_provider"); - - b.ToTable("tenant_payment_accounts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillingCycle") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("billing_cycle"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PlanCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("plan_code"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_subscriptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_subscriptions_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "ExpiresAt") - .HasDatabaseName("ix_tenant_subscriptions_tenant_id_status_expires_at"); - - b.ToTable("tenant_subscriptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MetricKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("metric_key"); - - b.Property("MetricValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("metric_value"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_tenant_usage_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_usage_records_tenant_id_id"); - - b.HasIndex("TenantId", "MetricKey", "PeriodStart", "PeriodEnd") - .HasDatabaseName("ix_tenant_usage_records_tenant_id_metric_key_period_start_peri~"); - - b.ToTable("tenant_usage_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContextPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("context_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("GeneratedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("generated_at"); - - b.Property("InputPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("input_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Model") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("model"); - - b.Property("PromptVersion") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("prompt_version"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("ResultPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("result_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_ai_recommendation_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_ai_recommendation_reports_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_ai_recommendation_reports_user_id"); - - b.HasIndex("TenantId", "RegionId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_region_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_user_id_created_at"); - - b.ToTable("ai_recommendation_reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetKey") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_app_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_app_assets_tenant_id_id"); - - b.HasIndex("TenantId", "AssetKey") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_asset_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_legacy_id"); - - b.ToTable("app_assets", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssetKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("AssetType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("asset_type"); - - b.Property("Bucket") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bucket"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("ChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("checksum_sha256"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DownloadCount") - .HasColumnType("integer") - .HasColumnName("download_count"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("FileSizeBytes") - .HasColumnType("bigint") - .HasColumnName("file_size_bytes"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MimeType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mime_type"); - - b.Property("ObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("object_key"); - - b.Property("PreviewObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("preview_object_key"); - - b.Property("PreviewStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("preview_status"); - - b.Property("PreviewUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_url"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SecurityFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScanProvider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("security_scan_provider"); - - b.Property("SecurityScanStatus") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("not_required") - .HasColumnName("security_scan_status"); - - b.Property("SecurityScanSummary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_scan_summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScannedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("security_scanned_at"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StorageProvider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("storage_provider"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UploadStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("upload_status"); - - b.Property("VerificationDetails") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("verification_details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.Property("VerifiedBy") - .HasColumnType("uuid") - .HasColumnName("verified_by"); - - b.Property("VerifiedChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("verified_checksum_sha256"); - - b.Property("VerifiedSizeBytes") - .HasColumnType("bigint") - .HasColumnName("verified_size_bytes"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_assets_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_assets_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_assets_updated_by"); - - b.HasIndex("VerifiedBy") - .HasDatabaseName("ix_content_assets_verified_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_content_assets_tenant_id_category_id"); - - b.HasIndex("TenantId", "ChecksumSha256") - .HasDatabaseName("ix_content_assets_tenant_id_checksum_sha256") - .HasFilter("checksum_sha256 is not null"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_content_assets_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_assets_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PreviewStatus") - .HasDatabaseName("ix_content_assets_tenant_id_preview_status") - .HasFilter("preview_status <> 'none'"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_assets_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_content_assets_tenant_id_subject_id"); - - b.HasIndex("TenantId", "StorageProvider", "Bucket", "ObjectKey") - .HasDatabaseName("ix_content_assets_tenant_id_storage_provider_bucket_object_key"); - - b.HasIndex("TenantId", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_upload_status_status_updated_at"); - - b.HasIndex("TenantId", "SecurityScanStatus", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_security_scan_status_upload_status~"); - - b.HasIndex("TenantId", "Status", "Visibility", "AssetType", "RegionId", "SubjectId", "CategoryId", "SortOrder") - .HasDatabaseName("ix_content_assets_tenant_id_status_visibility_asset_type_regio~"); - - b.ToTable("content_assets", null, t => - { - t.HasCheckConstraint("ck_content_assets_download_count", "download_count >= 0"); - - t.HasCheckConstraint("ck_content_assets_file_size", "file_size_bytes is null or file_size_bytes >= 0"); - - t.HasCheckConstraint("ck_content_assets_verified_checksum", "verified_checksum_sha256 is null or verified_checksum_sha256 ~ '^[a-f0-9]{64}$'"); - - t.HasCheckConstraint("ck_content_assets_verified_size", "verified_size_bytes is null or verified_size_bytes >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_type"); - - b.Property("ActorRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("actor_role"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("AssetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("asset_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DenyCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("deny_code"); - - b.Property("Disposition") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("disposition"); - - b.Property("ExpiresInSeconds") - .HasColumnType("integer") - .HasColumnName("expires_in_seconds"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("SignatureMode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("signature_mode"); - - b.Property("StorageProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("storage_provider"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("Visibility") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_asset_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_content_asset_access_events_user_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_asset_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_user_id_created_at") - .HasFilter("user_id is not null"); - - b.HasIndex("TenantId", "AccessType", "Result", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_access_type_result_cr~"); - - b.ToTable("content_asset_access_events", null, t => - { - t.HasCheckConstraint("ck_content_asset_access_events_expiry", "expires_in_seconds is null or expires_in_seconds > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.PrimitiveCollection("IssueCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("issue_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RiskLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("risk_level"); - - b.Property("ScanStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scan_status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_content_asset_security_scan_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_security_scan_events_tenant_id_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_asset_id_creat~"); - - b.HasIndex("TenantId", "ScanStatus", "RiskLevel", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_scan_status_ri~"); - - b.ToTable("content_asset_security_scan_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("description"); - - b.Property("EntryKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("entry_key"); - - b.Property("EntryType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entry_type"); - - b.Property("Icon") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LayoutConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("layout_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_entries_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_entries_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_entries_updated_by"); - - b.HasIndex("TenantId", "EntryKey") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_entry_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "EntryType", "IsActive", "SortOrder") - .HasDatabaseName("ix_content_entries_tenant_id_region_id_entry_type_is_active_so~"); - - b.ToTable("content_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("Message") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("message"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "ItemId") - .HasDatabaseName("ix_content_import_issues_tenant_id_item_id"); - - b.HasIndex("TenantId", "JobId", "Severity", "RowNo") - .HasDatabaseName("ix_content_import_issues_tenant_id_job_id_severity_row_no"); - - b.ToTable("content_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("content_hash"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExternalId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("external_id"); - - b.Property("IssuesCount") - .HasColumnType("integer") - .HasColumnName("issues_count"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("SourcePayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_items_tenant_id_id"); - - b.HasIndex("JobId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_content_import_items_job_id_row_no"); - - b.HasIndex("TenantId", "JobId", "Status", "RowNo") - .HasDatabaseName("ix_content_import_items_tenant_id_job_id_status_row_no"); - - b.ToTable("content_import_items", null, t => - { - t.HasCheckConstraint("ck_content_import_items_issues_count", "issues_count >= 0"); - - t.HasCheckConstraint("ck_content_import_items_row_no", "row_no >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DryRun") - .HasColumnType("boolean") - .HasColumnName("dry_run"); - - b.Property("ErrorCount") - .HasColumnType("integer") - .HasColumnName("error_count"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("ImportType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("import_type"); - - b.Property("InsertedCount") - .HasColumnType("integer") - .HasColumnName("inserted_count"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SkippedCount") - .HasColumnType("integer") - .HasColumnName("skipped_count"); - - b.Property("SourceFormat") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_format"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TargetCategoryId") - .HasColumnType("uuid") - .HasColumnName("target_category_id"); - - b.Property("TargetContentNodeId") - .HasColumnType("uuid") - .HasColumnName("target_content_node_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TargetRegionId") - .HasColumnType("uuid") - .HasColumnName("target_region_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedCount") - .HasColumnType("integer") - .HasColumnName("updated_count"); - - b.Property("ValidCount") - .HasColumnType("integer") - .HasColumnName("valid_count"); - - b.Property("WarningCount") - .HasColumnType("integer") - .HasColumnName("warning_count"); - - b.HasKey("Id") - .HasName("pk_content_import_jobs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_jobs_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_import_jobs_created_by"); - - b.HasIndex("TenantId", "TargetCategoryId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_category_id"); - - b.HasIndex("TenantId", "TargetContentNodeId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_content_node_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_question_bank_id"); - - b.HasIndex("TenantId", "TargetRegionId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_region_id"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_subject_id"); - - b.HasIndex("TenantId", "ImportType", "Status", "CreatedAt") - .HasDatabaseName("ix_content_import_jobs_tenant_id_import_type_status_created_at"); - - b.ToTable("content_import_jobs", null, t => - { - t.HasCheckConstraint("ck_content_import_jobs_counts", "total_count >= 0 and valid_count >= 0 and error_count >= 0 and warning_count >= 0 and inserted_count >= 0 and updated_count >= 0 and skipped_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Depth") - .HasColumnType("integer") - .HasColumnName("depth"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsLeaf") - .HasColumnType("boolean") - .HasColumnName("is_leaf"); - - b.Property("IsSelectable") - .HasColumnType("boolean") - .HasColumnName("is_selectable"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MarkerConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("marker_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MarkerType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("marker_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("node_key"); - - b.Property("NodeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("node_type"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasColumnType("ltree") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_content_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_nodes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_nodes_created_by"); - - b.HasIndex("Path") - .HasDatabaseName("ix_content_nodes_path"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Path"), "gist"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_nodes_updated_by"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MarkerType") - .HasDatabaseName("ix_content_nodes_tenant_id_marker_type") - .HasFilter("marker_type is not null"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_content_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_nodes_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "NodeKey") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_node_key"); - - b.HasIndex("TenantId", "EntryId", "ParentId", "SortOrder") - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_parent_id_sort_order"); - - b.ToTable("content_nodes", null, t => - { - t.HasCheckConstraint("ck_content_nodes_depth", "depth >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_chapters"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_chapters_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_chapters_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_subject_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_chapters_tenant_id_entry_id_content_node_id_subjec~"); - - b.ToTable("handbook_chapters", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChapterId") - .HasColumnType("uuid") - .HasColumnName("chapter_id"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_entries_tenant_id_id"); - - b.HasIndex("TenantId", "ChapterId") - .HasDatabaseName("ix_handbook_entries_tenant_id_chapter_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_entries_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "ChapterId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_entries_tenant_id_entry_id_content_node_id_chapter~"); - - b.ToTable("handbook_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_school_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_subjects_tenant_id_entry_id_content_node_id_region~"); - - b.ToTable("handbook_subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_images"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_images_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_images_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsPublic") - .HasDatabaseName("ix_images_tenant_id_category_is_public"); - - b.ToTable("images", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssemblyType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assembly_type"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PassScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("pass_score"); - - b.Property("QuestionLimit") - .HasColumnType("integer") - .HasColumnName("question_limit"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Rules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Sections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sections") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_practice_blueprints"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_blueprints_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_practice_blueprints_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_practice_blueprints_updated_by"); - - b.HasIndex("TenantId", "CollectionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_collection_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_practice_blueprints_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_region_id"); - - b.HasIndex("TenantId", "NodeId", "CollectionId", "Mode", "Status", "SortOrder") - .HasDatabaseName("ix_practice_blueprints_tenant_id_node_id_collection_id_mode_st~"); - - b.ToTable("practice_blueprints", null, t => - { - t.HasCheckConstraint("ck_practice_blueprints_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_practice_blueprints_question_limit", "question_limit is null or question_limit > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("AllowedPlanCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("allowed_plan_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.PrimitiveCollection("AllowedRegionIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_region_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedSubjectIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_subject_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedTenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("GrantScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("grant_scope"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_bank_grants"); - - b.HasIndex("AllowedPlanCodes") - .HasDatabaseName("ix_question_bank_grants_allowed_plan_codes"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedPlanCodes"), "gin"); - - b.HasIndex("AllowedTenantIds") - .HasDatabaseName("ix_question_bank_grants_allowed_tenant_ids"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedTenantIds"), "gin"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_bank_grants_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_bank_grants_updated_by"); - - b.HasIndex("SourceQuestionBankId", "Status", "StartsAt", "ExpiresAt") - .HasDatabaseName("ix_question_bank_grants_source_question_bank_id_status_starts_~"); - - b.ToTable("question_bank_grants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CollectionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("collection_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Filters") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("filters") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_collections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collections_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_collections_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_collections_updated_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_question_collections_tenant_id_category_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_question_collections_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_collections_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_question_collections_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_collections_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_question_collections_tenant_id_subject_id"); - - b.HasIndex("TenantId", "NodeId", "Status", "SortOrder") - .HasDatabaseName("ix_question_collections_tenant_id_node_id_status_sort_order"); - - b.ToTable("question_collections", null, t => - { - t.HasCheckConstraint("ck_question_collections_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_question_collections_question_count", "question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Required") - .HasColumnType("boolean") - .HasColumnName("required"); - - b.Property("Score") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_collection_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collection_items_tenant_id_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_question_collection_items_tenant_id_question_id"); - - b.HasIndex("TenantId", "CollectionId", "QuestionId") - .IsUnique() - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_question_~"); - - b.HasIndex("TenantId", "CollectionId", "SectionKey", "SortOrder") - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_section_k~"); - - b.ToTable("question_collection_items", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Types") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("types") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_type_groups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_type_groups_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_type_groups_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_question_type_groups_tenant_id_subject_id_is_active_sort_or~"); - - b.ToTable("question_type_groups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("LegacyVideoId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_video_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoId") - .HasColumnType("uuid") - .HasColumnName("video_id"); - - b.Property("VideoType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("video_type"); - - b.HasKey("Id") - .HasName("pk_question_videos"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_videos_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "VideoId") - .HasDatabaseName("ix_question_videos_tenant_id_video_id"); - - b.HasIndex("TenantId", "QuestionId", "VideoId", "VideoType") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_question_id_video_id_video_type"); - - b.ToTable("question_videos", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("SourceSubjectId") - .HasColumnType("uuid") - .HasColumnName("source_subject_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacySourceSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_source_subject_id"); - - b.Property("LegacyTargetSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_target_subject_id"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.HasKey("TenantId", "SourceSubjectId", "TargetSubjectId") - .HasName("pk_subject_shares"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_subject_shares_tenant_id_target_subject_id"); - - b.ToTable("subject_shares", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AdoptionMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("adoption_mode"); - - b.Property("CopiedQuestionCount") - .HasColumnType("integer") - .HasColumnName("copied_question_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("GrantId") - .HasColumnType("uuid") - .HasColumnName("grant_id"); - - b.Property("LastSyncedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_synced_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("SourceSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SyncStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("sync_status"); - - b.Property("TargetCollectionId") - .HasColumnType("uuid") - .HasColumnName("target_collection_id"); - - b.Property("TargetEntryId") - .HasColumnType("uuid") - .HasColumnName("target_entry_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_question_bank_adoptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_question_bank_adoptions_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_created_by"); - - b.HasIndex("GrantId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_grant_id"); - - b.HasIndex("SourceQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_source_question_bank_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_updated_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .IsUnique() - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_source_question_ba~"); - - b.HasIndex("TenantId", "TargetCollectionId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_collection_~"); - - b.HasIndex("TenantId", "TargetEntryId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_entry_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_question_ba~"); - - b.HasIndex("TenantId", "Status", "UpdatedAt") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_status_updated_at"); - - b.ToTable("tenant_question_bank_adoptions", null, t => - { - t.HasCheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FavoritedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("favorited_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Note") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.HasKey("Id") - .HasName("pk_user_word_favorites"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_favorites_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_favorites_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_favorites_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_favorites_tenant_id_user_id_word_id"); - - b.ToTable("user_word_favorites", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CorrectStreak") - .HasColumnType("integer") - .HasColumnName("correct_streak"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DueLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("due_level"); - - b.Property("EaseFactor") - .ValueGeneratedOnAdd() - .HasPrecision(4, 2) - .HasColumnType("numeric(4,2)") - .HasDefaultValue(2.50m) - .HasColumnName("ease_factor"); - - b.Property("LastResult") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("last_result"); - - b.Property("LastReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_review_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_review_at"); - - b.Property("ReviewCount") - .HasColumnType("integer") - .HasColumnName("review_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_user_word_progress"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_progress_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_progress_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_progress_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_word_id"); - - b.HasIndex("TenantId", "UserId", "NextReviewAt", "Status", "DueLevel") - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_next_review_at_status_~"); - - b.ToTable("user_word_progress", null, t => - { - t.HasCheckConstraint("ck_user_word_progress_correct_streak", "correct_streak >= 0"); - - t.HasCheckConstraint("ck_user_word_progress_ease_factor", "ease_factor >= 1.30 and ease_factor <= 3.00"); - - t.HasCheckConstraint("ck_user_word_progress_review_count", "review_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsGeneral") - .HasColumnType("boolean") - .HasColumnName("is_general"); - - b.Property("KnowledgeTags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("knowledge_tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ThumbnailUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("thumbnail_url"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("video_url"); - - b.HasKey("Id") - .HasName("pk_video_explanations"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_video_explanations_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_video_explanations_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SourceHash") - .HasDatabaseName("ix_video_explanations_tenant_id_source_hash") - .HasFilter("source_hash is not null"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_video_explanations_tenant_id_subject_id_is_active_sort_order"); - - b.ToTable("video_explanations", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WordCount") - .HasColumnType("integer") - .HasColumnName("word_count"); - - b.HasKey("Id") - .HasName("pk_vocabulary_units"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_units_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_units_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_units_tenant_id_entry_id_content_node_id_region_~"); - - b.ToTable("vocabulary_units", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Example") - .HasColumnType("text") - .HasColumnName("example"); - - b.Property("ExampleTranslation") - .HasColumnType("text") - .HasColumnName("example_translation"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Meaning") - .HasColumnType("text") - .HasColumnName("meaning"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phonetic") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("phonetic"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitId") - .HasColumnType("uuid") - .HasColumnName("unit_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Word") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("word"); - - b.HasKey("Id") - .HasName("pk_vocabulary_words"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_words_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_words_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_words_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UnitId", "IsActive", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_vocabulary_words_tenant_id_unit_id_is_active_sort_order_cre~"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "UnitId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_words_tenant_id_entry_id_content_node_id_unit_id~"); - - b.ToTable("vocabulary_words", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("EffectiveRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("effective_rate"); - - b.Property("GeneratedBy") - .HasColumnType("uuid") - .HasColumnName("generated_by"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidBy") - .HasColumnType("uuid") - .HasColumnName("paid_by"); - - b.Property("PaidUserCount") - .HasColumnType("integer") - .HasColumnName("paid_user_count"); - - b.Property("PaymentAccount") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("payment_account"); - - b.Property("PaymentMethod") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_method"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("SettlementNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("settlement_no"); - - b.Property("SourceCount") - .HasColumnType("integer") - .HasColumnName("source_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlements_tenant_id_id"); - - b.HasIndex("GeneratedBy") - .HasDatabaseName("ix_commission_settlements_generated_by"); - - b.HasIndex("PaidBy") - .HasDatabaseName("ix_commission_settlements_paid_by"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlements_referrer_user_id"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commission_settlements_reviewed_by"); - - b.HasIndex("TenantId", "SettlementNo") - .IsUnique() - .HasDatabaseName("ix_commission_settlements_tenant_id_settlement_no"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart") - .HasDatabaseName("ix_commission_settlements_referrer_start"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart", "PeriodEnd") - .IsUnique() - .HasDatabaseName("ux_commission_settlements_referrer_period"); - - b.ToTable("commission_settlements", null, t => - { - t.HasCheckConstraint("ck_commission_settlements_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_counts", "source_count >= 0 and paid_user_count >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_period", "period_end >= period_start"); - - t.HasCheckConstraint("ck_commission_settlements_rates", "default_rate >= 0 and default_rate <= 1 and (effective_rate is null or (effective_rate >= 0 and effective_rate <= 1))"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AttributionType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("attribution_type"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RateSource") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("rate_source"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("SettlementId") - .HasColumnType("uuid") - .HasColumnName("settlement_id"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_no"); - - b.Property("SourcePaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("source_paid_at"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlement_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlement_items_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlement_items_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_commission_settlement_items_student_user_id"); - - b.HasIndex("TenantId", "SettlementId") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_settlement_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "SourcePaidAt") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_referrer_user_id_sour~"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .IsUnique() - .HasDatabaseName("ix_commission_settlement_items_tenant_id_source_type_source_id"); - - b.ToTable("commission_settlement_items", null, t => - { - t.HasCheckConstraint("ck_commission_settlement_items_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlement_items_rate", "commission_rate >= 0 and commission_rate <= 1"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignmentConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssignmentCursor") - .HasColumnType("integer") - .HasColumnName("assignment_cursor"); - - b.Property("AssignmentMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("AssignmentPool") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_pool") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DelaySeconds") - .HasColumnType("integer") - .HasColumnName("delay_seconds"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("ExamType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("exam_type"); - - b.Property("FormName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("form_name"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Url") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("url"); - - b.HasKey("Id") - .HasName("pk_crm_config"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_config_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_crm_config_tenant_id"); - - b.HasIndex("TenantId", "AssignmentMode") - .HasDatabaseName("ix_crm_config_tenant_id_assignment_mode"); - - b.ToTable("crm_config", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempt") - .HasColumnType("integer") - .HasColumnName("attempt"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("HttpCode") - .HasColumnType("integer") - .HasColumnName("http_code"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Outcome") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("outcome"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("RequestBody") - .HasColumnType("text") - .HasColumnName("request_body"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ResponseSummary") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("response_summary"); - - b.Property("SignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("signed_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_log"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_log_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_log_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_log_tenant_id_record_id_created_at"); - - b.ToTable("crm_webhook_log", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("ScheduledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Source") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("target_url"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_queue"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_queue_tenant_id_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_idempotency_key") - .HasFilter("idempotency_key is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id_created_at") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "Source", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_source_created_at") - .HasFilter("source = 'tenant.student.crm_push'"); - - b.HasIndex("TenantId", "Status", "ScheduledAt", "NextAttemptAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_status_scheduled_at_next_attemp~"); - - b.ToTable("crm_webhook_queue", null, t => - { - t.HasCheckConstraint("ck_crm_webhook_queue_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LandingPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("landing_path"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_codes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_codes_user_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_code"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_user_id"); - - b.HasIndex("TenantId", "Code", "Status") - .HasDatabaseName("ix_referral_codes_tenant_id_code_status"); - - b.ToTable("referral_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("assigned_at"); - - b.Property("AssignedBy") - .HasColumnType("uuid") - .HasColumnName("assigned_by"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("AssignmentMode") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("BindType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bind_type"); - - b.Property("BoundAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("bound_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FirstTrackId") - .HasColumnType("uuid") - .HasColumnName("first_track_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ProtectedUntil") - .HasColumnType("timestamp with time zone") - .HasColumnName("protected_until"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_leads"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_leads_tenant_id_id"); - - b.HasIndex("AssignedBy") - .HasDatabaseName("ix_referral_leads_assigned_by"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_referral_leads_assigned_to_user_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_leads_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_referral_leads_student_user_id"); - - b.HasIndex("TenantId", "FirstTrackId") - .HasDatabaseName("ix_referral_leads_tenant_id_first_track_id"); - - b.HasIndex("TenantId", "StudentUserId") - .IsUnique() - .HasDatabaseName("ix_referral_leads_tenant_id_student_user_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_referrer_user_id_bound_at"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_assigned_to_user_id_status_bound_at"); - - b.ToTable("referral_leads", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Page") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("page"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("QrcodeUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("qrcode_url"); - - b.Property("RefCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("Scene") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("scene"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_qrcodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_qrcodes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_qrcodes_user_id"); - - b.HasIndex("TenantId", "RefCode", "Status") - .HasDatabaseName("ix_referral_qrcodes_tenant_id_ref_code_status"); - - b.HasIndex("TenantId", "Provider", "Scene", "Page") - .IsUnique() - .HasDatabaseName("ix_referral_qrcodes_tenant_id_provider_scene_page"); - - b.ToTable("referral_qrcodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LeaderUserId") - .HasColumnType("uuid") - .HasColumnName("leader_user_id"); - - b.Property("MemberUserId") - .HasColumnType("uuid") - .HasColumnName("member_user_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RelationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("relation_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_team_edges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_team_edges_tenant_id_id"); - - b.HasIndex("LeaderUserId") - .HasDatabaseName("ix_referral_team_edges_leader_user_id"); - - b.HasIndex("MemberUserId") - .HasDatabaseName("ix_referral_team_edges_member_user_id"); - - b.HasIndex("TenantId", "LeaderUserId", "Status") - .HasDatabaseName("ix_referral_team_edges_tenant_id_leader_user_id_status"); - - b.HasIndex("TenantId", "MemberUserId", "RelationType") - .IsUnique() - .HasDatabaseName("ix_referral_team_edges_tenant_id_member_user_id_relation_type"); - - b.ToTable("referral_team_edges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("LeadId") - .HasColumnType("uuid") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("TargetUserId") - .HasColumnType("uuid") - .HasColumnName("target_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_referral_tracks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_tracks_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_tracks_referrer_user_id"); - - b.HasIndex("TargetUserId") - .HasDatabaseName("ix_referral_tracks_target_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_referral_tracks_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "LeadId", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_lead_id_created_at"); - - b.HasIndex("TenantId", "RefCode", "EventType", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_ref_code_event_type_created_at"); - - b.ToTable("referral_tracks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("MinSettlementCents") - .HasColumnType("integer") - .HasColumnName("min_settlement_cents"); - - b.Property("SettlementCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("settlement_cycle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_commission_settings"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_commission_settings_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_commission_settings_tenant_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_commission_settings_updated_by"); - - b.ToTable("tenant_commission_settings", null, t => - { - t.HasCheckConstraint("ck_tenant_commission_settings_default_rate", "default_rate >= 0 and default_rate <= 1"); - - t.HasCheckConstraint("ck_tenant_commission_settings_min_settlement", "min_settlement_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AvatarPreset") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("male") - .HasColumnName("avatar_preset"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastCheckInDate") - .HasColumnType("date") - .HasColumnName("last_check_in_date"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("MasteredWordsCount") - .HasColumnType("integer") - .HasColumnName("mastered_words_count"); - - b.Property("ModuleSelections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_selections") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Progress") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("progress") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionsAnsweredToday") - .HasColumnType("integer") - .HasColumnName("questions_answered_today"); - - b.Property("RecentActivities") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("recent_activities") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SelectedMajorId") - .HasColumnType("uuid") - .HasColumnName("selected_major_id"); - - b.Property("SelectedSchoolId") - .HasColumnType("uuid") - .HasColumnName("selected_school_id"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_student_profiles"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_student_profiles_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_student_profiles_user_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_student_profiles_tenant_id_region_id"); - - b.HasIndex("TenantId", "SelectedMajorId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_major_id"); - - b.HasIndex("TenantId", "SelectedSchoolId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_school_id"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_student_profiles_tenant_id_user_id"); - - b.ToTable("student_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AvatarUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("avatar_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("LastSeenAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPasswordHash") - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasColumnName("legacy_password_hash"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PasswordMigrationRequired") - .HasColumnType("boolean") - .HasColumnName("password_migration_required"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("PrimaryRole") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("primary_role"); - - b.Property("RawProfile") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_profile") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Score") - .HasColumnType("integer") - .HasColumnName("score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Username") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Email") - .IsUnique() - .HasDatabaseName("ix_users_email"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_users_legacy_id"); - - b.HasIndex("Phone") - .IsUnique() - .HasDatabaseName("ix_users_phone"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("OpenId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("open_id"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderSubject") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("provider_subject"); - - b.Property("SecretPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("secret_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UnionId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("union_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_identities"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_identities_user_id"); - - b.HasIndex("Provider", "ProviderSubject") - .IsUnique() - .HasDatabaseName("ix_user_identities_provider_provider_subject"); - - b.ToTable("user_identities", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("IssueCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("RawValueSample") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("raw_value_sample"); - - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_run_id"); - - b.HasIndex("RunId", "Severity", "CollectionName") - .HasDatabaseName("ix_pb_import_issues_run_id_severity_collection_name"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("SourceKind") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_kind"); - - b.Property("SourceName") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_runs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_runs_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "StartedAt") - .HasDatabaseName("ix_pb_import_runs_tenant_id_status_started_at"); - - b.ToTable("pb_import_runs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("CollectionName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Errors") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("errors") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ImportedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("imported_at") - .HasDefaultValueSql("now()"); - - b.Property("Normalized") - .HasColumnType("boolean") - .HasColumnName("normalized"); - - b.Property("Record") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("record") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("RunId", "CollectionName", "LegacyId") - .HasName("pk_pb_raw_records"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_run_id"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_raw_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("AnsweredAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("answered_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsCorrect") - .HasColumnType("boolean") - .HasColumnName("is_correct"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("QuestionVersionId") - .HasColumnType("uuid") - .HasColumnName("question_version_id"); - - b.Property("SelectedOptions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("selected_options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_answer_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_answer_records_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_answer_records_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_answer_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id"); - - b.HasIndex("TenantId", "QuestionId", "QuestionVersionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id_question_version_id"); - - b.HasIndex("TenantId", "UserId", "PracticeSessionId") - .HasDatabaseName("ix_answer_records_tenant_id_user_id_practice_session_id"); - - b.ToTable("answer_records", null, t => - { - t.HasCheckConstraint("ck_answer_records_version_requires_question", "question_version_id is null or question_id is not null"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActiveUsers") - .HasColumnType("integer") - .HasColumnName("active_users"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("NewOrders") - .HasColumnType("integer") - .HasColumnName("new_orders"); - - b.Property("NewQuestions") - .HasColumnType("integer") - .HasColumnName("new_questions"); - - b.Property("NewRevenueCents") - .HasColumnType("integer") - .HasColumnName("new_revenue_cents"); - - b.Property("NewUsers") - .HasColumnType("integer") - .HasColumnName("new_users"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_dashboard_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_dashboard_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_stat_date_region_id"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId"), false); - - b.ToTable("dashboard_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("ExamAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("exam_at"); - - b.Property("ExamName") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("exam_name"); - - b.Property("ExamType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("exam_type"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_exam_dates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_exam_dates_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_exam_dates_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_exam_dates_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId", "ExamAt") - .HasDatabaseName("ix_exam_dates_tenant_id_school_id_exam_at"); - - b.ToTable("exam_dates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_favorite_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_favorite_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_favorite_questions_tenant_id_question_id"); - - b.ToTable("favorite_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_mode"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementId") - .HasColumnType("uuid") - .HasColumnName("entitlement_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("GrantedCount") - .HasColumnType("integer") - .HasColumnName("granted_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("Reason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("reason"); - - b.Property("RequestedCount") - .HasColumnType("integer") - .HasColumnName("requested_count"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_access_events_user_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_access_events_tenant_id_practice_session_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_practice_access_events_tenant_id_user_id_created_at"); - - b.ToTable("practice_access_events", null, t => - { - t.HasCheckConstraint("ck_practice_access_events_consumed_free_quota", "consumed_free_quota >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_granted_count", "granted_count >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_requested_count", "requested_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FreeLimit") - .HasColumnType("integer") - .HasColumnName("free_limit"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsageDate") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasColumnName("usage_date") - .HasDefaultValueSql("current_date"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_daily_usage"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_daily_usage_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_daily_usage_user_id"); - - b.HasIndex("TenantId", "UserId", "UsageDate") - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date"); - - b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId") - .IsUnique() - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date_scope_typ~"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId"), false); - - b.ToTable("practice_daily_usage", null, t => - { - t.HasCheckConstraint("ck_practice_daily_usage_free_limit", "free_limit >= 0"); - - t.HasCheckConstraint("ck_practice_daily_usage_used_count", "used_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessEntitlementId") - .HasColumnType("uuid") - .HasColumnName("access_entitlement_id"); - - b.Property("AccessMode") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("free") - .HasColumnName("access_mode"); - - b.Property("AccessSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_sessions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "UserId", "Id") - .HasName("ak_practice_sessions_tenant_id_user_id_id"); - - 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"); - - b.ToTable("practice_sessions", null, t => - { - t.HasCheckConstraint("ck_practice_sessions_consumed_free_quota", "consumed_free_quota >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionResults") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_results") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionStats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("section_stats") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("SubmittedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("submitted_at") - .HasDefaultValueSql("now()"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalQuestions") - .HasColumnType("integer") - .HasColumnName("total_questions"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.Property("WrongQuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("wrong_question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.HasKey("Id") - .HasName("pk_practice_session_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_reports_tenant_id_id"); - - 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.ToTable("practice_session_reports", null, t => - { - t.HasCheckConstraint("ck_practice_session_reports_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_reports_counts", "total_questions >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_duration", "duration_seconds >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("question_type"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SectionName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("section_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_practice_session_report_sections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_report_sections_tenant_id_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_practice_session~"); - - b.HasIndex("TenantId", "ReportId", "SectionKey") - .IsUnique() - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sectio~"); - - b.HasIndex("TenantId", "ReportId", "SortOrder") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sort_o~"); - - b.ToTable("practice_session_report_sections", null, t => - { - t.HasCheckConstraint("ck_practice_session_report_sections_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_report_sections_counts", "question_count >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_report_sections_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastAccessAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_access_at"); - - b.Property("LastPracticeAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_practice_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("practice_type"); - - b.Property("Progress") - .HasColumnType("integer") - .HasColumnName("progress"); - - b.Property("TargetLegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("target_legacy_id"); - - b.Property("TargetName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("target_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_recent_practices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_recent_practices_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_recent_practices_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_recent_practices_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "LastAccessAt") - .HasDatabaseName("ix_recent_practices_tenant_id_user_id_last_access_at"); - - b.ToTable("recent_practices", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attachments") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("attachments") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("Contact") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("contact"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("HandledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("handled_at"); - - b.Property("HandledBy") - .HasColumnType("uuid") - .HasColumnName("handled_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Resolution") - .HasColumnType("text") - .HasColumnName("resolution"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_reports_tenant_id_id"); - - b.HasIndex("HandledBy") - .HasDatabaseName("ix_reports_handled_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_reports_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_reports_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_question_id_created_at") - .HasFilter("question_id is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_status_created_at"); - - b.ToTable("reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_report_status_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_report_status_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_report_status_events_actor_user_id"); - - b.HasIndex("TenantId", "ReportId", "CreatedAt") - .HasDatabaseName("ix_report_status_events_tenant_id_report_id_created_at"); - - b.ToTable("report_status_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CodeCount") - .HasColumnType("integer") - .HasColumnName("code_count"); - - b.Property("CodeEstimated") - .HasColumnType("integer") - .HasColumnName("code_estimated"); - - b.Property("CodeUsed") - .HasColumnType("integer") - .HasColumnName("code_used"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EstimatedRevenueCents") - .HasColumnType("integer") - .HasColumnName("estimated_revenue_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("OrderCount") - .HasColumnType("integer") - .HasColumnName("order_count"); - - b.Property("RealRevenueCents") - .HasColumnType("integer") - .HasColumnName("real_revenue_cents"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_revenue_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_revenue_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_stat_date_region_id_sale_type"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType"), false); - - b.ToTable("revenue_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceAfter") - .HasColumnType("integer") - .HasColumnName("balance_after"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Points") - .HasColumnType("integer") - .HasColumnName("points"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_score_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_score_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_score_events_user_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_user_score_events_tenant_id_idempotency_key"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_user_score_events_tenant_id_user_id_created_at"); - - b.ToTable("user_score_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("LastWrongAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_wrong_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("WrongCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(1) - .HasColumnName("wrong_count"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_wrong_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_wrong_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_wrong_questions_tenant_id_question_id"); - - b.ToTable("wrong_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_announcements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_announcements_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_announcements_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "IsActive", "SortOrder") - .HasDatabaseName("ix_announcements_tenant_id_is_active_sort_order"); - - b.ToTable("announcements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_audit_logs"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_audit_logs_actor_user_id"); - - b.HasIndex("TenantId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_created_at"); - - b.HasIndex("TenantId", "TargetType", "TargetId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_target_type_target_id_created_at"); - - b.ToTable("audit_logs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("ConditionExtra") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("condition_extra") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ConditionField") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("condition_field"); - - b.Property("ConditionOperator") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("condition_operator"); - - b.Property("ConditionValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("condition_value"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon_url"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Level") - .HasColumnType("integer") - .HasColumnName("level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnlockType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("unlock_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_badges_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsActive", "SortOrder") - .HasDatabaseName("ix_badges_tenant_id_category_is_active_sort_order"); - - b.ToTable("badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("BorderColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("border_color"); - - b.Property("ButtonLink") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("button_link"); - - b.Property("ButtonText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("button_text"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Subtitle") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("subtitle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_banners"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_banners_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_banners_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_banners_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("banners", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Answer") - .HasColumnType("text") - .HasColumnName("answer"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Question") - .HasColumnType("text") - .HasColumnName("question"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_faqs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_faqs_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_faqs_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_faqs_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("faqs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("AdoptionId") - .HasColumnType("uuid") - .HasColumnName("adoption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("ReadBy") - .HasColumnType("uuid") - .HasColumnName("read_by"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_content_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_content_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_content_notifications_created_by"); - - b.HasIndex("ReadBy") - .HasDatabaseName("ix_tenant_content_notifications_read_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_source_question_bank~"); - - b.HasIndex("TenantId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_notification_type_de~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "AdoptionId", "NotificationType", "Status") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_adoption_id_notifica~"); - - b.ToTable("tenant_content_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActivePublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ActiveTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("active_template_code"); - - b.Property("ActiveTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DraftPublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("draft_template_code"); - - b.Property("DraftTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftUpdatedBy") - .HasColumnType("uuid") - .HasColumnName("draft_updated_by"); - - b.Property("PublishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("published_at"); - - b.Property("PublishedBy") - .HasColumnType("uuid") - .HasColumnName("published_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_configs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_theme_configs_tenant_id_id"); - - b.HasIndex("ActiveTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_active_template_code"); - - b.HasIndex("DraftTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_draft_template_code"); - - b.HasIndex("DraftUpdatedBy") - .HasDatabaseName("ix_tenant_theme_configs_draft_updated_by"); - - b.HasIndex("PublishedBy") - .HasDatabaseName("ix_tenant_theme_configs_published_by"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_configs_tenant_id"); - - b.ToTable("tenant_theme_configs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PreviewImageUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_image_url"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_templates"); - - b.HasAlternateKey("Code") - .HasName("ak_tenant_theme_templates_code"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_templates_code"); - - b.HasIndex("Status", "SortOrder", "Code") - .HasDatabaseName("ix_tenant_theme_templates_status_sort_order_code"); - - b.ToTable("tenant_theme_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BadgeId") - .HasColumnType("uuid") - .HasColumnName("badge_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrantedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("granted_at"); - - b.Property("GrantedBy") - .HasColumnType("uuid") - .HasColumnName("granted_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_badges_tenant_id_id"); - - b.HasIndex("GrantedBy") - .HasDatabaseName("ix_user_badges_granted_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_badges_user_id"); - - b.HasIndex("TenantId", "BadgeId") - .HasDatabaseName("ix_user_badges_tenant_id_badge_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "BadgeId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_user_id_badge_id"); - - b.ToTable("user_badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_user_notifications_created_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_notifications_user_id"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .HasDatabaseName("ix_user_notifications_tenant_id_source_type_source_id"); - - b.HasIndex("TenantId", "UserId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_notification_type_dedu~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "UserId", "Status", "CreatedAt") - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_status_created_at"); - - b.ToTable("user_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("AcknowledgedBy") - .HasColumnType("uuid") - .HasColumnName("acknowledged_by"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("AuditLogId") - .HasColumnType("uuid") - .HasColumnName("audit_log_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FirstSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("first_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("LastSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("RuleId") - .HasColumnType("uuid") - .HasColumnName("rule_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alerts"); - - b.HasIndex("AcknowledgedBy") - .HasDatabaseName("ix_platform_audit_alerts_acknowledged_by"); - - b.HasIndex("AuditLogId") - .HasDatabaseName("ix_platform_audit_alerts_audit_log_id"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_platform_audit_alerts_resolved_by"); - - b.HasIndex("RuleId", "AuditLogId") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alerts_rule_id_audit_log_id"); - - b.HasIndex("Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_status_severity_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_tenant_id_status_created_at"); - - b.ToTable("platform_audit_alerts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("ActionPatterns") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("action_patterns") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("Conditions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("conditions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.PrimitiveCollection("TargetTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("target_types") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alert_rules"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alert_rules_code"); - - b.HasIndex("TenantId") - .HasDatabaseName("ix_platform_audit_alert_rules_tenant_id"); - - b.HasIndex("Enabled", "Severity", "Code") - .HasDatabaseName("ix_platform_audit_alert_rules_enabled_severity_code"); - - b.ToTable("platform_audit_alert_rules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationChannel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChannelCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel_code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MinReminderLevel") - .HasColumnType("integer") - .HasColumnName("min_reminder_level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.PrimitiveCollection("ReminderChannels") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_channels") - .HasDefaultValueSql("array['internal']::text[]"); - - b.PrimitiveCollection("ReminderTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_types") - .HasDefaultValueSql("array['overdue', 'final_notice']::text[]"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.PrimitiveCollection("TenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WebhookUrl") - .IsRequired() - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("webhook_url"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_channels"); - - b.HasIndex("ChannelCode") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_channels_channel_code"); - - b.HasIndex("Enabled", "MinReminderLevel", "ChannelCode") - .HasDatabaseName("ix_platform_dunning_notification_channels_enabled_min_reminder~"); - - b.ToTable("platform_dunning_notification_channels", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_channels_level", "min_reminder_level between 1 and 20"); - - t.HasCheckConstraint("ck_platform_dunning_channels_timeout", "timeout_seconds between 1 and 60"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("ChannelId") - .HasColumnType("uuid") - .HasColumnName("channel_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.Property("ReminderId") - .HasColumnType("uuid") - .HasColumnName("reminder_id"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScheduledAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at") - .HasDefaultValueSql("now()"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_platform_dunning_notification_events_tenant_id_id"); - - b.HasIndex("ChannelId", "ReminderId") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_events_channel_id_reminder_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_invoice_id"); - - b.HasIndex("TenantId", "ReminderId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_reminder_id"); - - b.HasIndex("InvoiceId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_invoice_id_status_crea~"); - - b.HasIndex("ReminderId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_reminder_id_status_cre~"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_status_creat~"); - - b.HasIndex("Status", "NextAttemptAt", "ScheduledAt", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_status_next_attempt_at~"); - - b.ToTable("platform_dunning_notification_events", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_events_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformSaasPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseAmountCents") - .HasColumnType("integer") - .HasColumnName("base_amount_cents"); - - b.Property("BillingCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_cycle"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IncludedQuotas") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("included_quotas") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OveragePrices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("overage_prices") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_saas_plans"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_saas_plans_code"); - - b.HasIndex("Status", "SortOrder") - .HasDatabaseName("ix_platform_saas_plans_status_sort_order"); - - b.ToTable("platform_saas_plans", null, t => - { - t.HasCheckConstraint("ck_platform_saas_plans_amount", "base_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BankAccountMasked") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("bank_account_masked"); - - b.Property("BankName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bank_name"); - - b.Property("BillingAddress") - .HasColumnType("text") - .HasColumnName("billing_address"); - - b.Property("BillingName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("billing_name"); - - b.Property("ContactEmail") - .HasMaxLength(300) - .HasColumnType("citext") - .HasColumnName("contact_email"); - - b.Property("ContactName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("contact_name"); - - b.Property("ContactPhone") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("contact_phone"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceTitle") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("invoice_title"); - - b.Property("InvoiceType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TaxId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("tax_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_billing_profiles"); - - b.ToTable("tenant_billing_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceCents") - .HasColumnType("integer") - .HasColumnName("balance_cents"); - - b.Property("BillingPeriodEnd") - .HasColumnType("date") - .HasColumnName("billing_period_end"); - - b.Property("BillingPeriodStart") - .HasColumnType("date") - .HasColumnName("billing_period_start"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("DiscountCents") - .HasColumnType("integer") - .HasColumnName("discount_cents"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("invoice_no"); - - b.Property("InvoiceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidCents") - .HasColumnType("integer") - .HasColumnName("paid_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubtotalCents") - .HasColumnType("integer") - .HasColumnName("subtotal_cents"); - - b.Property("TaxCents") - .HasColumnType("integer") - .HasColumnName("tax_cents"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCents") - .HasColumnType("integer") - .HasColumnName("total_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoices_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoices_created_by"); - - b.HasIndex("InvoiceNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_invoice_no"); - - b.HasIndex("TenantId", "BillingPeriodStart", "BillingPeriodEnd") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_tenant_id_billing_period_start_billing_peri~") - .HasFilter("invoice_type = 'usage_overage' and status <> 'void' and metadata->>'source' = 'usage_overage_auto'"); - - b.HasIndex("TenantId", "Status", "DueDate") - .HasDatabaseName("ix_tenant_invoices_tenant_id_status_due_date"); - - b.ToTable("tenant_invoices", null, t => - { - t.HasCheckConstraint("ck_tenant_invoices_amounts", "subtotal_cents >= 0 and discount_cents >= 0 and tax_cents >= 0 and total_cents >= 0 and paid_cents >= 0 and balance_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoices_period", "billing_period_end is null or billing_period_start is null or billing_period_end >= billing_period_start"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("description"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("ItemRefId") - .HasColumnType("uuid") - .HasColumnName("item_ref_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Quantity") - .HasPrecision(12, 2) - .HasColumnType("numeric(12,2)") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_items_tenant_id_id"); - - b.HasIndex("InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_invoice_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_items", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_items_amounts", "unit_amount_cents >= 0 and amount_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_no"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReceivedBy") - .HasColumnType("uuid") - .HasColumnName("received_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_payments_tenant_id_id"); - - b.HasIndex("PaymentNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_payments_payment_no"); - - b.HasIndex("ReceivedBy") - .HasDatabaseName("ix_tenant_invoice_payments_received_by"); - - b.HasIndex("InvoiceId", "Status") - .HasDatabaseName("ix_tenant_invoice_payments_invoice_id_status"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_payments_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_payments", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("BalanceCentsSnapshot") - .HasColumnType("integer") - .HasColumnName("balance_cents_snapshot"); - - b.Property("Channel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("channel"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Message") - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReminderDate") - .HasColumnType("date") - .HasColumnName("reminder_date"); - - b.Property("ReminderLevel") - .HasColumnType("integer") - .HasColumnName("reminder_level"); - - b.Property("ReminderType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("reminder_type"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_reminders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_reminders_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoice_reminders_created_by"); - - b.HasIndex("InvoiceId", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_invoice_id_reminder_date"); - - b.HasIndex("TenantId", "Status", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_status_reminder_date"); - - b.HasIndex("TenantId", "InvoiceId", "ReminderType", "Channel", "ReminderDate") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_invoice_id_reminder_type~"); - - b.ToTable("tenant_invoice_reminders", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_reminders_balance", "balance_cents_snapshot >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_reminders_level", "reminder_level between 1 and 20"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CurrentVersionId") - .HasColumnType("uuid") - .HasColumnName("current_version_id"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExamMarkers") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("exam_markers") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("HasVideoExplanation") - .HasColumnType("boolean") - .HasColumnName("has_video_explanation"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyNodeId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_node_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("MediaUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("media_url"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PrimaryCollectionId") - .HasColumnType("uuid") - .HasColumnName("primary_collection_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("TypeLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("type_label"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_questions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_questions_tenant_id_id"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_questions_tenant_id_category_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_questions_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_questions_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_questions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_questions_tenant_id_node_id"); - - b.HasIndex("TenantId", "PrimaryCollectionId") - .HasDatabaseName("ix_questions_tenant_id_primary_collection_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_questions_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_questions_tenant_id_subject_id"); - - b.HasIndex("TenantId", "Id", "CurrentVersionId") - .HasDatabaseName("ix_questions_tenant_id_id_current_version_id"); - - b.ToTable("questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SourceScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_scope"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_banks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_banks_tenant_id_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_banks_tenant_id_region_id"); - - b.ToTable("question_banks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("CodeLang") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code_lang"); - - b.Property("CodeTemplate") - .HasColumnType("text") - .HasColumnName("code_template"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CorrectOptionIndex") - .HasColumnType("integer") - .HasColumnName("correct_option_index"); - - b.Property("CorrectOptionIndices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("correct_option_indices") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Explanation") - .HasColumnType("text") - .HasColumnName("explanation"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubQuestions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sub_questions") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("VersionNo") - .HasColumnType("integer") - .HasColumnName("version_no"); - - b.HasKey("Id") - .HasName("pk_question_versions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_versions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "QuestionId", "Id") - .HasName("ak_question_versions_tenant_id_question_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_versions_created_by"); - - b.HasIndex("QuestionId", "VersionNo") - .IsUnique() - .HasDatabaseName("ix_question_versions_question_id_version_no"); - - b.ToTable("question_versions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FailureCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("failure_code"); - - b.Property("Identifier") - .HasMaxLength(320) - .HasColumnType("character varying(320)") - .HasColumnName("identifier"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_login_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_login_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_login_events_user_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_auth_login_events_tenant_id_user_id_created_at"); - - b.ToTable("auth_login_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TokenHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("token_hash"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_sessions_tenant_id_id"); - - b.HasIndex("TokenHash") - .IsUnique() - .HasDatabaseName("ix_auth_sessions_token_hash"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_sessions_user_id"); - - b.HasIndex("TenantId", "UserId", "ExpiresAt") - .HasDatabaseName("ix_auth_sessions_tenant_id_user_id_expires_at") - .HasFilter("revoked_at is null"); - - b.ToTable("auth_sessions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Dimension") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("dimension"); - - b.Property("ScopeHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("scope_hash"); - - b.Property("BucketStart") - .HasColumnType("timestamp with time zone") - .HasColumnName("bucket_start"); - - b.Property("RequestCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(0) - .HasColumnName("request_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId", "Dimension", "ScopeHash", "BucketStart") - .HasName("pk_sms_send_rate_limits"); - - b.HasIndex("UpdatedAt") - .HasDatabaseName("ix_sms_send_rate_limits_updated_at"); - - b.ToTable("sms_send_rate_limits", null, t => - { - t.HasCheckConstraint("ck_sms_send_rate_limits_request_count", "request_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CodeHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("code_hash"); - - b.Property("ConsumedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("consumed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phone") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Purpose") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("purpose"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_sms_verification_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_sms_verification_codes_tenant_id_id"); - - b.HasIndex("TenantId", "Phone", "Purpose") - .IsUnique() - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "CreatedAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_created_at") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "ExpiresAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_expires_at"); - - b.ToTable("sms_verification_codes", null, t => - { - t.HasCheckConstraint("ck_sms_verification_codes_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BillingStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_status"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegalName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("legal_name"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OwnerUserId") - .HasColumnType("uuid") - .HasColumnName("owner_user_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("slug"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenants"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenants_legacy_id"); - - b.HasIndex("OwnerUserId") - .HasDatabaseName("ix_tenants_owner_user_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_tenants_slug"); - - b.ToTable("tenants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_auth_providers"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_auth_providers_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider"); - - b.HasIndex("TenantId", "Provider", "Status") - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider_status"); - - b.ToTable("tenant_auth_providers", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BrandName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("brand_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FaviconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("favicon_url"); - - b.Property("LogoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("logo_url"); - - b.Property("OrganizationName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("organization_name"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ServiceAccountName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("service_account_name"); - - b.Property("ServiceWechat") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("service_wechat"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("Slogan") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("slogan"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_branding"); - - b.ToTable("tenant_branding", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_classes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_classes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_classes_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_classes_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_code") - .HasFilter("code is not null and code <> ''"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_tenant_classes_tenant_id_region_id") - .HasFilter("region_id is not null"); - - b.HasIndex("TenantId", "Status", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_tenant_classes_tenant_id_status_sort_order_created_at"); - - b.ToTable("tenant_classes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("JoinedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("joined_at") - .HasDefaultValueSql("now()"); - - b.Property("LeftAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("left_at"); - - b.Property("MemberType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("member_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_class_members"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_class_members_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_class_members_user_id"); - - b.HasIndex("TenantId", "ClassId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_status_member_type"); - - b.HasIndex("TenantId", "ClassId", "UserId", "MemberType") - .IsUnique() - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_user_id_member_type"); - - b.HasIndex("TenantId", "UserId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_user_id_status_member_type"); - - b.ToTable("tenant_class_members", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DomainType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("domain_type"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(253) - .HasColumnType("citext") - .HasColumnName("host"); - - b.Property("IsPrimary") - .HasColumnType("boolean") - .HasColumnName("is_primary"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VerificationToken") - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("verification_token"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.HasKey("Id") - .HasName("pk_tenant_domains"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_domains_tenant_id_id"); - - b.HasIndex("Host") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_host"); - - b.HasIndex("TenantId", "IsPrimary") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_tenant_id_is_primary") - .HasFilter("is_primary"); - - b.ToTable("tenant_domains", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyRole") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("legacy_role"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Role") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("role"); - - b.Property("RoleTemplateId") - .HasColumnType("uuid") - .HasColumnName("role_template_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_memberships"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_memberships_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_memberships_user_id"); - - b.HasIndex("TenantId", "RoleTemplateId") - .HasDatabaseName("ix_tenant_memberships_tenant_id_role_template_id"); - - b.HasIndex("TenantId", "UserId", "Role") - .IsUnique() - .HasDatabaseName("ix_tenant_memberships_tenant_id_user_id_role"); - - b.ToTable("tenant_memberships", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("base_role"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DataScope") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("data_scope") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FieldPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IsSystem") - .HasColumnType("boolean") - .HasColumnName("is_system"); - - b.Property("MenuPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("menu_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModulePermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_role_templates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_role_templates_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_role_templates_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_role_templates_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_role_templates_tenant_id_code"); - - b.HasIndex("TenantId", "Status", "SortOrder") - .HasDatabaseName("ix_tenant_role_templates_tenant_id_status_sort_order"); - - b.ToTable("tenant_role_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("AdminFeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("admin_feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PublicConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_settings"); - - b.ToTable("tenant_settings", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CompletedBy") - .HasColumnType("uuid") - .HasColumnName("completed_by"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("FollowupType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("followup_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_student_followups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_followups_tenant_id_id"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_tenant_student_followups_assigned_to_user_id"); - - b.HasIndex("CompletedBy") - .HasDatabaseName("ix_tenant_student_followups_completed_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_followups_student_user_id"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_assigned_to_user_id_stat~") - .HasFilter("assigned_to_user_id is not null"); - - b.HasIndex("TenantId", "ClassId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_class_id_status_due_at") - .HasFilter("class_id is not null"); - - b.HasIndex("TenantId", "StudentUserId", "Status", "DueAt", "CreatedAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_student_user_id_status_d~"); - - b.ToTable("tenant_student_followups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("IsPinned") - .HasColumnType("boolean") - .HasColumnName("is_pinned"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NoteType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("note_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_tenant_student_notes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_notes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_student_notes_created_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_notes_student_user_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_student_notes_updated_by"); - - b.HasIndex("TenantId", "CreatedBy", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_created_by_created_at") - .HasFilter("created_by is not null"); - - b.HasIndex("TenantId", "StudentUserId", "IsPinned", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_student_user_id_is_pinned_cr~"); - - b.ToTable("tenant_student_notes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_categories_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_majors_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_module_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_module_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_regions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_region_modules_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_region_modules_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_schools_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineField", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_scoreline_fields_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_fields_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_scoreline_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AgentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_agent_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_activation_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UsedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_used_by"); - - b.HasOne("Tiku.Domain.Commerce.CodeBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_code_batches_tenant_id_batch_id"); - - b.HasOne("Tiku.Domain.Commerce.CouponRedemption", null) - .WithMany() - .HasForeignKey("TenantId", "CouponRedemptionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_coupon_redemptions_tenant_id_coupon_redemp~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "UsedRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_regions_tenant_id_used_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_code_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_batches_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedTo") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_assigned_to"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_resolved_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_batc~"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_item~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_refund_requests_ten~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issue_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationIssue", null) - .WithMany() - .HasForeignKey("TenantId", "IssueId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_commerce_reconciliatio~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_commerce_reconciliation_batch~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_commerce_refund_requests_tena~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_commerce_refund_requests_tenant_id_r~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ProcessedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_processed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RequestedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_requested_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_refund_requests_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupons_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_coupons_svip_plans_tenant_id_plan_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupon_redemptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.Coupon", null) - .WithMany() - .HasForeignKey("TenantId", "CouponId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_coupons_tenant_id_coupon_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RevokedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_entitlements_users_revoked_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_orders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_orders_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payment_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_payment_events_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_products_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_products_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_svip_plans_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_svip_plans_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_payment_accounts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_subscriptions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_usage_records_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_ai_recommendation_reports_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_app_assets_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_assets_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("VerifiedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_verified_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_content_nodes_tenant_id_content_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_asset_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_access_events_content_assets_tenant_id_asset_~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_security_scan_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_security_scan_events_content_assets_tenant_id~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_entries_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_import_issues_content_import_items_tenant_id_item_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_import_jobs_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_jobs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_categories_tenant_id_target_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "TargetContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_content_nodes_tenant_id_target_content_~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_question_banks_tenant_id_target_questio~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "TargetRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_regions_tenant_id_target_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_nodes_content_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_chapters_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_chapters_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_handbook_chapters_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.HandbookSubject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_chapters_handbook_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.HandbookChapter", null) - .WithMany() - .HasForeignKey("TenantId", "ChapterId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_entries_handbook_chapters_tenant_id_chapter_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_entries_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_handbook_entries_content_entries_tenant_id_entry_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_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_handbook_subjects_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_images_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_blueprints_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_question_collections_tenant_id_collecti~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_bank_grants_question_banks_source_question_bank_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_question_banks_tenant_id_question_bank~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_question_collections_tenant_id_co~"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_type_groups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_type_groups_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_videos_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.Content.VideoExplanation", null) - .WithMany() - .HasForeignKey("TenantId", "VideoId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_video_explanations_tenant_id_video_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SourceSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_source_subject_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_created_by"); - - b.HasOne("Tiku.Domain.Content.QuestionBankGrant", null) - .WithMany() - .HasForeignKey("GrantId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_bank_grants_grant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_source_questi~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_collections_tenant_~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "TargetEntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_content_entries_tenant_id_ta~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_tenant_id_tar~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_video_explanations_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_video_explanations_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_units_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_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_vocabulary_units_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_words_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_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_vocabulary_words_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyUnit", null) - .WithMany() - .HasForeignKey("TenantId", "UnitId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_vocabulary_units_tenant_id_unit_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GeneratedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_generated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PaidBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_paid_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.CommissionSettlement", null) - .WithMany() - .HasForeignKey("TenantId", "SettlementId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_commission_settlements_tenant_i~"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_config_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_log_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_queue_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralTrack", null) - .WithMany() - .HasForeignKey("TenantId", "FirstTrackId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_referral_tracks_tenant_id_first_track_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_qrcodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_qrcodes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("LeaderUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_team_edges_users_leader_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("MemberUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_users_member_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("TargetUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_target_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_tracks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralLead", null) - .WithMany() - .HasForeignKey("TenantId", "LeadId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_referral_leads_tenant_id_lead_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_commission_settings_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_commission_settings_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedMajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_majors_tenant_id_selected_major_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedSchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_schools_tenant_id_selected_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_identities_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_pb_import_issues_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_runs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId", "QuestionVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_question_versions_tenant_id_question_id_ques~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "UserId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "UserId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_practice_sessions_tenant_id_user_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_dashboard_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_dashboard_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_exam_dates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_practice_sessions_tenant_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .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.PracticeSessionReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .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") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_practice_sessions_tenant_id_practi~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_sessions_tenant_i~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSessionReport", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_session_reports_t~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_recent_practices_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_recent_practices_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("HandledBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_handled_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_reports_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_report_status_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.Report", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_reports_tenant_id_report_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_revenue_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_revenue_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_announcements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_audit_logs_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_audit_logs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_badges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_banners_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_banners_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_faqs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_faqs_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReadBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_read_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_content_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "SourceQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_tenant_content_notifications_question_banks_tenant_id_sourc~"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("ActiveTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_active_template~"); - - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("DraftTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_draft_template_~"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("DraftUpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_draft_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PublishedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_published_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_theme_configs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GrantedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_badges_users_granted_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_badges_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_users_user_id"); - - b.HasOne("Tiku.Domain.Operations.Badge", null) - .WithMany() - .HasForeignKey("TenantId", "BadgeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_badges_tenant_id_badge_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AcknowledgedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_acknowledged_by"); - - b.HasOne("Tiku.Domain.Operations.AuditLog", null) - .WithMany() - .HasForeignKey("AuditLogId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_audit_logs_audit_log_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_resolved_by"); - - b.HasOne("Tiku.Domain.Platform.PlatformAuditAlertRule", null) - .WithMany() - .HasForeignKey("RuleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_platform_audit_alert_rules_rule_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alerts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alert_rules_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.HasOne("Tiku.Domain.Platform.PlatformDunningNotificationChannel", null) - .WithMany() - .HasForeignKey("ChannelId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_platform_dunning_notif~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoices_tenant~"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoiceReminder", null) - .WithMany() - .HasForeignKey("TenantId", "ReminderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoice_reminde~"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Platform.TenantBillingProfile", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_billing_profiles_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoices_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoices_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReceivedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_payments_users_received_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_reminders_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenant_invoices_tenant_id_invoice_~"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_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_questions_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "PrimaryCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_collections_tenant_id_primary_collection~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_banks_tenant_id_question_bank_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_subjects_tenant_id_subject_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "Id", "CurrentVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_versions_tenant_id_id_current_version_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_banks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_banks_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_versions_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_versions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_login_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_auth_login_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_send_rate_limits_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_verification_codes_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("OwnerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenants_users_owner_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_auth_providers_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantBranding", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_branding_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_classes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_classes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_domains_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantRoleTemplate", null) - .WithMany() - .HasForeignKey("TenantId", "RoleTemplateId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_role_templates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantSettings", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_settings_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CompletedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_completed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_updated_by"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726103534_AddStudentProfileAvatarPreset.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726103534_AddStudentProfileAvatarPreset.cs deleted file mode 100644 index 3c02041..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726103534_AddStudentProfileAvatarPreset.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - /// - public partial class AddStudentProfileAvatarPreset : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "avatar_preset", - table: "student_profiles", - type: "character varying(32)", - maxLength: 32, - nullable: false, - defaultValue: "male"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "avatar_preset", - table: "student_profiles"); - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726112706_AddPaymentSdkAndTenantSecretFoundation.Designer.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726112706_AddPaymentSdkAndTenantSecretFoundation.Designer.cs deleted file mode 100644 index 4fc5fe7..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726112706_AddPaymentSdkAndTenantSecretFoundation.Designer.cs +++ /dev/null @@ -1,15660 +0,0 @@ -// -using System; -using System.Text.Json; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using Tiku.Infrastructure.Persistence; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - [DbContext(typeof(TikuDbContext))] - [Migration("20260726112706_AddPaymentSdkAndTenantSecretFoundation")] - partial class AddPaymentSdkAndTenantSecretFoundation - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext"); - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "ltree"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("category_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("SvipQuestionLimit") - .HasColumnType("integer") - .HasColumnName("svip_question_limit"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_categories"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_categories_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_categories_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_categories_tenant_id_node_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_categories_tenant_id_subject_id"); - - b.ToTable("categories", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("StudyTips") - .HasColumnType("text") - .HasColumnName("study_tips"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_majors"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_majors_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_majors_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_majors_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_majors_tenant_id_school_id"); - - b.ToTable("majors", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyModuleId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_module_id"); - - b.Property("LegacyParentId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_parent_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_module_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_module_nodes_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_module_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_module_nodes_tenant_id_module_id"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_module_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_module_nodes_tenant_id_region_id"); - - b.ToTable("module_nodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FullName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("full_name"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsHot") - .HasColumnType("boolean") - .HasColumnName("is_hot"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Pinyin") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("pinyin"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_regions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_regions_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_regions_tenant_id_legacy_id"); - - b.ToTable("regions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsPrimarySchoolModule") - .HasColumnType("boolean") - .HasColumnName("is_primary_school_module"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TextColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("text_color"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_region_modules"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_region_modules_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_region_modules_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_region_modules_tenant_id_region_id"); - - b.ToTable("region_modules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ProfessionalExamDate") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("professional_exam_date"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_schools"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_schools_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_schools_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_schools_tenant_id_module_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_schools_tenant_id_region_id"); - - b.ToTable("schools", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineField", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FieldKey") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("field_key"); - - b.Property("FieldName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("field_name"); - - b.Property("FieldType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("field_type"); - - b.Property("IsFilter") - .HasColumnType("boolean") - .HasColumnName("is_filter"); - - b.Property("IsRequired") - .HasColumnType("boolean") - .HasColumnName("is_required"); - - b.Property("IsTrend") - .HasColumnType("boolean") - .HasColumnName("is_trend"); - - b.Property("IsVisible") - .HasColumnType("boolean") - .HasColumnName("is_visible"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Placeholder") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("placeholder"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Unit") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("unit"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_scoreline_fields"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_scoreline_fields_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_scoreline_fields_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "FieldKey") - .IsUnique() - .HasDatabaseName("ix_scoreline_fields_tenant_id_region_id_field_key"); - - b.HasIndex("TenantId", "RegionId", "IsFilter", "SortOrder") - .HasDatabaseName("ix_scoreline_fields_tenant_id_region_id_is_filter_sort_order"); - - b.ToTable("scoreline_fields", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldValues") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_values") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("major_name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SchoolName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("school_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Year") - .HasColumnType("integer") - .HasColumnName("year"); - - b.HasKey("Id") - .HasName("pk_scoreline_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_scoreline_records_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_scoreline_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_scoreline_records_tenant_id_major_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_scoreline_records_tenant_id_school_id"); - - b.HasIndex("TenantId", "Year") - .HasDatabaseName("ix_scoreline_records_tenant_id_year"); - - b.HasIndex("TenantId", "RegionId", "SchoolId", "MajorId", "Year") - .HasDatabaseName("ix_scoreline_records_tenant_id_region_id_school_id_major_id_ye~"); - - b.ToTable("scoreline_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_subjects_tenant_id_module_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_subjects_tenant_id_node_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_subjects_tenant_id_school_id"); - - b.ToTable("subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AgentUserId") - .HasColumnType("uuid") - .HasColumnName("agent_user_id"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponRedemptionId") - .HasColumnType("uuid") - .HasColumnName("coupon_redemption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("IsUsed") - .HasColumnType("boolean") - .HasColumnName("is_used"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("SoldTo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("sold_to"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitPriceCents") - .HasColumnType("integer") - .HasColumnName("unit_price_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UsedBy") - .HasColumnType("uuid") - .HasColumnName("used_by"); - - b.Property("UsedRegionId") - .HasColumnType("uuid") - .HasColumnName("used_region_id"); - - b.HasKey("Id") - .HasName("pk_activation_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_activation_codes_tenant_id_id"); - - b.HasIndex("AgentUserId") - .HasDatabaseName("ix_activation_codes_agent_user_id"); - - b.HasIndex("UsedBy") - .HasDatabaseName("ix_activation_codes_used_by"); - - b.HasIndex("TenantId", "BatchId") - .HasDatabaseName("ix_activation_codes_tenant_id_batch_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_code"); - - b.HasIndex("TenantId", "CouponRedemptionId") - .HasDatabaseName("ix_activation_codes_tenant_id_coupon_redemption_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UsedRegionId") - .HasDatabaseName("ix_activation_codes_tenant_id_used_region_id"); - - b.ToTable("activation_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CampaignName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("campaign_name"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CostPriceCents") - .HasColumnType("integer") - .HasColumnName("cost_price_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("DefaultUnitPriceCents") - .HasColumnType("integer") - .HasColumnName("default_unit_price_cents"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_code_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_code_batches_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_code_batches_tenant_id_legacy_id"); - - b.ToTable("code_batches", null, t => - { - t.HasCheckConstraint("ck_code_batches_amounts", "default_unit_price_cents >= 0 and cost_price_cents >= 0"); - - t.HasCheckConstraint("ck_code_batches_counts", "total_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillDate") - .HasColumnType("date") - .HasColumnName("bill_date"); - - b.Property("BillType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bill_type"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DuplicateCount") - .HasColumnType("integer") - .HasColumnName("duplicate_count"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IgnoredCount") - .HasColumnType("integer") - .HasColumnName("ignored_count"); - - b.Property("MatchedCount") - .HasColumnType("integer") - .HasColumnName("matched_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MismatchCount") - .HasColumnType("integer") - .HasColumnName("mismatch_count"); - - b.Property("MissingLocalCount") - .HasColumnType("integer") - .HasColumnName("missing_local_count"); - - b.Property("MissingProviderCount") - .HasColumnType("integer") - .HasColumnName("missing_provider_count"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source"); - - b.Property("SourceHash") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("source_name"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_batches_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_batches_created_by"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "Provider", "BillDate", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_provider_bill_dat~"); - - b.ToTable("commerce_reconciliation_batches", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_batches_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_batches_counts", "total_count >= 0 and matched_count >= 0 and mismatch_count >= 0 and missing_local_count >= 0 and missing_provider_count >= 0 and duplicate_count >= 0 and ignored_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("AssignedTo") - .HasColumnType("uuid") - .HasColumnName("assigned_to"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("IssueNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_no"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolutionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("resolution_type"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("summary"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issues_tenant_id_id"); - - b.HasIndex("AssignedTo") - .HasDatabaseName("ix_commerce_reconciliation_issues_assigned_to"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_created_by"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_resolved_by"); - - b.HasIndex("TenantId", "IssueNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_issue_no"); - - b.HasIndex("TenantId", "ItemId") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_item_id") - .HasFilter("item_id is not null and status in ('open', 'investigating', 'escalated')"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "PaymentId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_payment_id_created~"); - - b.HasIndex("TenantId", "AssignedTo", "Status", "DueAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_assigned_to_status~"); - - b.HasIndex("TenantId", "BatchId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_batch_id_status_cr~"); - - b.HasIndex("TenantId", "Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_status_severity_cr~"); - - b.ToTable("commerce_reconciliation_issues", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_issues_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("IssueId") - .HasColumnType("uuid") - .HasColumnName("issue_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issue_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issue_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_actor_user_id"); - - b.HasIndex("TenantId", "IssueId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_tenant_id_issue_id_cre~"); - - b.ToTable("commerce_reconciliation_issue_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LocalStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("local_status"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider_status"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("RefundedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("refunded_at"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_items_tenant_id_id"); - - b.HasIndex("BatchId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_items_batch_id_row_no"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "BatchId", "MatchStatus", "RowNo") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_batch_id_match_stat~"); - - b.HasIndex("TenantId", "Provider", "ProviderTradeNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_provider_provider_t~"); - - b.HasIndex("TenantId", "RefundNo", "ProviderRefundNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_no_provider_~"); - - b.ToTable("commerce_reconciliation_items", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_items_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_items_row", "row_no > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_refund_events_actor_user_id"); - - b.HasIndex("TenantId", "RefundRequestId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_events_tenant_id_refund_request_id_created_~"); - - b.ToTable("commerce_refund_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CancelledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("cancelled_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementAction") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entitlement_action"); - - b.Property("FailedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("failed_at"); - - b.Property("FailureReason") - .HasColumnType("text") - .HasColumnName("failure_reason"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("ProcessedBy") - .HasColumnType("uuid") - .HasColumnName("processed_by"); - - b.Property("Provider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("Reason") - .HasColumnType("text") - .HasColumnName("reason"); - - b.Property("RefundNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RequestedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("requested_at") - .HasDefaultValueSql("now()"); - - b.Property("RequestedBy") - .HasColumnType("uuid") - .HasColumnName("requested_by"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SucceededAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("succeeded_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_requests"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_requests_tenant_id_id"); - - b.HasIndex("ProcessedBy") - .HasDatabaseName("ix_commerce_refund_requests_processed_by"); - - b.HasIndex("RequestedBy") - .HasDatabaseName("ix_commerce_refund_requests_requested_by"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commerce_refund_requests_reviewed_by"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundNo") - .IsUnique() - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_refund_no"); - - b.HasIndex("TenantId", "OrderId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_order_id_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_status_created_at"); - - b.ToTable("commerce_refund_requests", null, t => - { - t.HasCheckConstraint("ck_commerce_refund_requests_amount", "amount_cents > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("discount_type"); - - b.Property("DiscountValue") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("discount_value"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MaxUses") - .HasColumnType("integer") - .HasColumnName("max_uses"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("ValidFrom") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_from"); - - b.Property("ValidTo") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_to"); - - b.HasKey("Id") - .HasName("pk_coupons"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupons_tenant_id_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_code"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupons_tenant_id_plan_id"); - - b.ToTable("coupons", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClaimedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("claimed_at"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponId") - .HasColumnType("uuid") - .HasColumnName("coupon_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountAppliedCents") - .HasColumnType("integer") - .HasColumnName("discount_applied_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_coupon_redemptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupon_redemptions_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_coupon_redemptions_user_id"); - - b.HasIndex("TenantId", "CouponId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_coupon_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_region_id"); - - b.HasIndex("TenantId", "OrderId", "UserId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_order_id_user_id") - .HasFilter("order_id is not null"); - - b.HasIndex("TenantId", "UserId", "CouponId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_user_id_coupon_id") - .HasFilter("coupon_id is not null"); - - b.ToTable("coupon_redemptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("entitlement_type"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("LegacySourceId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_source_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("RevokedBy") - .HasColumnType("uuid") - .HasColumnName("revoked_by"); - - b.Property("RevokedReason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("revoked_reason"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("StartsAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at") - .HasDefaultValueSql("now()"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_entitlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_entitlements_tenant_id_id"); - - b.HasIndex("RevokedBy") - .HasDatabaseName("ix_entitlements_revoked_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_entitlements_user_id"); - - b.HasIndex("TenantId", "UserId", "Status", "ExpiresAt") - .HasDatabaseName("ix_entitlements_tenant_id_user_id_status_expires_at"); - - b.ToTable("entitlements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPlanId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_plan_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("OrderNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PayMethod") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_method"); - - b.Property("PayProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_provider"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("ProductName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("product_name"); - - b.Property("ProductType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("product_type"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("trade_no"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_orders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_orders_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_orders_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderNo") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_order_no"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_orders_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_orders_tenant_id_region_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_orders_tenant_id_user_id_created_at"); - - b.ToTable("orders", null, t => - { - t.HasCheckConstraint("ck_orders_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_orders_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("Quantity") - .HasColumnType("integer") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalAmountCents") - .HasColumnType("integer") - .HasColumnName("total_amount_cents"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_order_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_order_items_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_order_items_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_order_items_tenant_id_order_id"); - - b.ToTable("order_items", null, t => - { - t.HasCheckConstraint("ck_order_items_amounts", "unit_amount_cents >= 0 and total_amount_cents >= 0"); - - t.HasCheckConstraint("ck_order_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyOrderId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_order_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payments_tenant_id_id"); - - b.HasIndex("Provider", "ProviderTradeNo") - .IsUnique() - .HasDatabaseName("ix_payments_provider_provider_trade_no") - .HasFilter("provider_trade_no is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_payments_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId", "UpdatedAt") - .HasDatabaseName("ix_payments_tenant_id_order_id_updated_at"); - - b.ToTable("payments", null, t => - { - t.HasCheckConstraint("ck_payments_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_payments_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("EventId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("event_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("SignatureValid") - .HasColumnType("boolean") - .HasColumnName("signature_valid"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_payment_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payment_events_tenant_id_id"); - - b.HasIndex("Provider", "EventId") - .IsUnique() - .HasDatabaseName("ix_payment_events_provider_event_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_payment_events_tenant_id_payment_id"); - - b.ToTable("payment_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Cover") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cover"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DetailImages") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("detail_images") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true) - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPriceText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_price_text"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("PreviewIframe") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_iframe"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_products"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_products_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_products_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "Type", "IsActive", "SortOrder") - .HasDatabaseName("ix_products_tenant_id_region_id_type_is_active_sort_order"); - - b.ToTable("products", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Badge") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("badge"); - - b.Property("CouponOnly") - .HasColumnType("boolean") - .HasColumnName("coupon_only"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OriginalPriceCents") - .HasColumnType("integer") - .HasColumnName("original_price_cents"); - - b.Property("PerDayLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("per_day_label"); - - b.Property("PriceCents") - .HasColumnType("integer") - .HasColumnName("price_cents"); - - b.Property("Recommended") - .HasColumnType("boolean") - .HasColumnName("recommended"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VpEnabled") - .HasColumnType("boolean") - .HasColumnName("vp_enabled"); - - b.Property("VpProductId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("vp_product_id"); - - b.HasKey("Id") - .HasName("pk_svip_plans"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_svip_plans_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_svip_plans_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_svip_plans_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("svip_plans", null, t => - { - t.HasCheckConstraint("ck_svip_plans_days", "days >= 0"); - - t.HasCheckConstraint("ck_svip_plans_price", "price_cents >= 0 and (original_price_cents is null or original_price_cents >= 0)"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_payment_accounts"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_payment_accounts_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_payment_accounts_tenant_id_provider"); - - b.ToTable("tenant_payment_accounts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillingCycle") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("billing_cycle"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PlanCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("plan_code"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_subscriptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_subscriptions_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "ExpiresAt") - .HasDatabaseName("ix_tenant_subscriptions_tenant_id_status_expires_at"); - - b.ToTable("tenant_subscriptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MetricKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("metric_key"); - - b.Property("MetricValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("metric_value"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_tenant_usage_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_usage_records_tenant_id_id"); - - b.HasIndex("TenantId", "MetricKey", "PeriodStart", "PeriodEnd") - .HasDatabaseName("ix_tenant_usage_records_tenant_id_metric_key_period_start_peri~"); - - b.ToTable("tenant_usage_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContextPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("context_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("GeneratedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("generated_at"); - - b.Property("InputPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("input_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Model") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("model"); - - b.Property("PromptVersion") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("prompt_version"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("ResultPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("result_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_ai_recommendation_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_ai_recommendation_reports_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_ai_recommendation_reports_user_id"); - - b.HasIndex("TenantId", "RegionId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_region_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_user_id_created_at"); - - b.ToTable("ai_recommendation_reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetKey") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_app_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_app_assets_tenant_id_id"); - - b.HasIndex("TenantId", "AssetKey") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_asset_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_legacy_id"); - - b.ToTable("app_assets", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssetKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("AssetType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("asset_type"); - - b.Property("Bucket") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bucket"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("ChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("checksum_sha256"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DownloadCount") - .HasColumnType("integer") - .HasColumnName("download_count"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("FileSizeBytes") - .HasColumnType("bigint") - .HasColumnName("file_size_bytes"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MimeType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mime_type"); - - b.Property("ObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("object_key"); - - b.Property("PreviewObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("preview_object_key"); - - b.Property("PreviewStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("preview_status"); - - b.Property("PreviewUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_url"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SecurityFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScanProvider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("security_scan_provider"); - - b.Property("SecurityScanStatus") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("not_required") - .HasColumnName("security_scan_status"); - - b.Property("SecurityScanSummary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_scan_summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScannedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("security_scanned_at"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StorageProvider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("storage_provider"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UploadStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("upload_status"); - - b.Property("VerificationDetails") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("verification_details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.Property("VerifiedBy") - .HasColumnType("uuid") - .HasColumnName("verified_by"); - - b.Property("VerifiedChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("verified_checksum_sha256"); - - b.Property("VerifiedSizeBytes") - .HasColumnType("bigint") - .HasColumnName("verified_size_bytes"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_assets_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_assets_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_assets_updated_by"); - - b.HasIndex("VerifiedBy") - .HasDatabaseName("ix_content_assets_verified_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_content_assets_tenant_id_category_id"); - - b.HasIndex("TenantId", "ChecksumSha256") - .HasDatabaseName("ix_content_assets_tenant_id_checksum_sha256") - .HasFilter("checksum_sha256 is not null"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_content_assets_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_assets_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PreviewStatus") - .HasDatabaseName("ix_content_assets_tenant_id_preview_status") - .HasFilter("preview_status <> 'none'"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_assets_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_content_assets_tenant_id_subject_id"); - - b.HasIndex("TenantId", "StorageProvider", "Bucket", "ObjectKey") - .HasDatabaseName("ix_content_assets_tenant_id_storage_provider_bucket_object_key"); - - b.HasIndex("TenantId", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_upload_status_status_updated_at"); - - b.HasIndex("TenantId", "SecurityScanStatus", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_security_scan_status_upload_status~"); - - b.HasIndex("TenantId", "Status", "Visibility", "AssetType", "RegionId", "SubjectId", "CategoryId", "SortOrder") - .HasDatabaseName("ix_content_assets_tenant_id_status_visibility_asset_type_regio~"); - - b.ToTable("content_assets", null, t => - { - t.HasCheckConstraint("ck_content_assets_download_count", "download_count >= 0"); - - t.HasCheckConstraint("ck_content_assets_file_size", "file_size_bytes is null or file_size_bytes >= 0"); - - t.HasCheckConstraint("ck_content_assets_verified_checksum", "verified_checksum_sha256 is null or verified_checksum_sha256 ~ '^[a-f0-9]{64}$'"); - - t.HasCheckConstraint("ck_content_assets_verified_size", "verified_size_bytes is null or verified_size_bytes >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_type"); - - b.Property("ActorRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("actor_role"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("AssetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("asset_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DenyCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("deny_code"); - - b.Property("Disposition") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("disposition"); - - b.Property("ExpiresInSeconds") - .HasColumnType("integer") - .HasColumnName("expires_in_seconds"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("SignatureMode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("signature_mode"); - - b.Property("StorageProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("storage_provider"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("Visibility") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_asset_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_content_asset_access_events_user_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_asset_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_user_id_created_at") - .HasFilter("user_id is not null"); - - b.HasIndex("TenantId", "AccessType", "Result", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_access_type_result_cr~"); - - b.ToTable("content_asset_access_events", null, t => - { - t.HasCheckConstraint("ck_content_asset_access_events_expiry", "expires_in_seconds is null or expires_in_seconds > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.PrimitiveCollection("IssueCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("issue_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RiskLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("risk_level"); - - b.Property("ScanStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scan_status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_content_asset_security_scan_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_security_scan_events_tenant_id_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_asset_id_creat~"); - - b.HasIndex("TenantId", "ScanStatus", "RiskLevel", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_scan_status_ri~"); - - b.ToTable("content_asset_security_scan_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("description"); - - b.Property("EntryKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("entry_key"); - - b.Property("EntryType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entry_type"); - - b.Property("Icon") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LayoutConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("layout_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_entries_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_entries_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_entries_updated_by"); - - b.HasIndex("TenantId", "EntryKey") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_entry_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "EntryType", "IsActive", "SortOrder") - .HasDatabaseName("ix_content_entries_tenant_id_region_id_entry_type_is_active_so~"); - - b.ToTable("content_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("Message") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("message"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "ItemId") - .HasDatabaseName("ix_content_import_issues_tenant_id_item_id"); - - b.HasIndex("TenantId", "JobId", "Severity", "RowNo") - .HasDatabaseName("ix_content_import_issues_tenant_id_job_id_severity_row_no"); - - b.ToTable("content_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("content_hash"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExternalId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("external_id"); - - b.Property("IssuesCount") - .HasColumnType("integer") - .HasColumnName("issues_count"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("SourcePayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_items_tenant_id_id"); - - b.HasIndex("JobId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_content_import_items_job_id_row_no"); - - b.HasIndex("TenantId", "JobId", "Status", "RowNo") - .HasDatabaseName("ix_content_import_items_tenant_id_job_id_status_row_no"); - - b.ToTable("content_import_items", null, t => - { - t.HasCheckConstraint("ck_content_import_items_issues_count", "issues_count >= 0"); - - t.HasCheckConstraint("ck_content_import_items_row_no", "row_no >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DryRun") - .HasColumnType("boolean") - .HasColumnName("dry_run"); - - b.Property("ErrorCount") - .HasColumnType("integer") - .HasColumnName("error_count"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("ImportType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("import_type"); - - b.Property("InsertedCount") - .HasColumnType("integer") - .HasColumnName("inserted_count"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SkippedCount") - .HasColumnType("integer") - .HasColumnName("skipped_count"); - - b.Property("SourceFormat") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_format"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TargetCategoryId") - .HasColumnType("uuid") - .HasColumnName("target_category_id"); - - b.Property("TargetContentNodeId") - .HasColumnType("uuid") - .HasColumnName("target_content_node_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TargetRegionId") - .HasColumnType("uuid") - .HasColumnName("target_region_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedCount") - .HasColumnType("integer") - .HasColumnName("updated_count"); - - b.Property("ValidCount") - .HasColumnType("integer") - .HasColumnName("valid_count"); - - b.Property("WarningCount") - .HasColumnType("integer") - .HasColumnName("warning_count"); - - b.HasKey("Id") - .HasName("pk_content_import_jobs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_jobs_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_import_jobs_created_by"); - - b.HasIndex("TenantId", "TargetCategoryId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_category_id"); - - b.HasIndex("TenantId", "TargetContentNodeId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_content_node_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_question_bank_id"); - - b.HasIndex("TenantId", "TargetRegionId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_region_id"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_subject_id"); - - b.HasIndex("TenantId", "ImportType", "Status", "CreatedAt") - .HasDatabaseName("ix_content_import_jobs_tenant_id_import_type_status_created_at"); - - b.ToTable("content_import_jobs", null, t => - { - t.HasCheckConstraint("ck_content_import_jobs_counts", "total_count >= 0 and valid_count >= 0 and error_count >= 0 and warning_count >= 0 and inserted_count >= 0 and updated_count >= 0 and skipped_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Depth") - .HasColumnType("integer") - .HasColumnName("depth"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsLeaf") - .HasColumnType("boolean") - .HasColumnName("is_leaf"); - - b.Property("IsSelectable") - .HasColumnType("boolean") - .HasColumnName("is_selectable"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MarkerConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("marker_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MarkerType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("marker_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("node_key"); - - b.Property("NodeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("node_type"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasColumnType("ltree") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_content_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_nodes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_nodes_created_by"); - - b.HasIndex("Path") - .HasDatabaseName("ix_content_nodes_path"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Path"), "gist"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_nodes_updated_by"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MarkerType") - .HasDatabaseName("ix_content_nodes_tenant_id_marker_type") - .HasFilter("marker_type is not null"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_content_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_nodes_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "NodeKey") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_node_key"); - - b.HasIndex("TenantId", "EntryId", "ParentId", "SortOrder") - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_parent_id_sort_order"); - - b.ToTable("content_nodes", null, t => - { - t.HasCheckConstraint("ck_content_nodes_depth", "depth >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_chapters"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_chapters_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_chapters_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_subject_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_chapters_tenant_id_entry_id_content_node_id_subjec~"); - - b.ToTable("handbook_chapters", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChapterId") - .HasColumnType("uuid") - .HasColumnName("chapter_id"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_entries_tenant_id_id"); - - b.HasIndex("TenantId", "ChapterId") - .HasDatabaseName("ix_handbook_entries_tenant_id_chapter_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_entries_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "ChapterId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_entries_tenant_id_entry_id_content_node_id_chapter~"); - - b.ToTable("handbook_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_school_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_subjects_tenant_id_entry_id_content_node_id_region~"); - - b.ToTable("handbook_subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_images"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_images_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_images_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsPublic") - .HasDatabaseName("ix_images_tenant_id_category_is_public"); - - b.ToTable("images", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssemblyType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assembly_type"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PassScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("pass_score"); - - b.Property("QuestionLimit") - .HasColumnType("integer") - .HasColumnName("question_limit"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Rules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Sections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sections") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_practice_blueprints"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_blueprints_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_practice_blueprints_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_practice_blueprints_updated_by"); - - b.HasIndex("TenantId", "CollectionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_collection_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_practice_blueprints_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_region_id"); - - b.HasIndex("TenantId", "NodeId", "CollectionId", "Mode", "Status", "SortOrder") - .HasDatabaseName("ix_practice_blueprints_tenant_id_node_id_collection_id_mode_st~"); - - b.ToTable("practice_blueprints", null, t => - { - t.HasCheckConstraint("ck_practice_blueprints_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_practice_blueprints_question_limit", "question_limit is null or question_limit > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("AllowedPlanCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("allowed_plan_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.PrimitiveCollection("AllowedRegionIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_region_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedSubjectIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_subject_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedTenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("GrantScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("grant_scope"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_bank_grants"); - - b.HasIndex("AllowedPlanCodes") - .HasDatabaseName("ix_question_bank_grants_allowed_plan_codes"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedPlanCodes"), "gin"); - - b.HasIndex("AllowedTenantIds") - .HasDatabaseName("ix_question_bank_grants_allowed_tenant_ids"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedTenantIds"), "gin"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_bank_grants_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_bank_grants_updated_by"); - - b.HasIndex("SourceQuestionBankId", "Status", "StartsAt", "ExpiresAt") - .HasDatabaseName("ix_question_bank_grants_source_question_bank_id_status_starts_~"); - - b.ToTable("question_bank_grants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CollectionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("collection_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Filters") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("filters") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_collections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collections_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_collections_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_collections_updated_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_question_collections_tenant_id_category_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_question_collections_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_collections_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_question_collections_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_collections_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_question_collections_tenant_id_subject_id"); - - b.HasIndex("TenantId", "NodeId", "Status", "SortOrder") - .HasDatabaseName("ix_question_collections_tenant_id_node_id_status_sort_order"); - - b.ToTable("question_collections", null, t => - { - t.HasCheckConstraint("ck_question_collections_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_question_collections_question_count", "question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Required") - .HasColumnType("boolean") - .HasColumnName("required"); - - b.Property("Score") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_collection_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collection_items_tenant_id_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_question_collection_items_tenant_id_question_id"); - - b.HasIndex("TenantId", "CollectionId", "QuestionId") - .IsUnique() - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_question_~"); - - b.HasIndex("TenantId", "CollectionId", "SectionKey", "SortOrder") - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_section_k~"); - - b.ToTable("question_collection_items", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Types") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("types") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_type_groups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_type_groups_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_type_groups_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_question_type_groups_tenant_id_subject_id_is_active_sort_or~"); - - b.ToTable("question_type_groups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("LegacyVideoId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_video_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoId") - .HasColumnType("uuid") - .HasColumnName("video_id"); - - b.Property("VideoType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("video_type"); - - b.HasKey("Id") - .HasName("pk_question_videos"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_videos_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "VideoId") - .HasDatabaseName("ix_question_videos_tenant_id_video_id"); - - b.HasIndex("TenantId", "QuestionId", "VideoId", "VideoType") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_question_id_video_id_video_type"); - - b.ToTable("question_videos", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("SourceSubjectId") - .HasColumnType("uuid") - .HasColumnName("source_subject_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacySourceSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_source_subject_id"); - - b.Property("LegacyTargetSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_target_subject_id"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.HasKey("TenantId", "SourceSubjectId", "TargetSubjectId") - .HasName("pk_subject_shares"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_subject_shares_tenant_id_target_subject_id"); - - b.ToTable("subject_shares", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AdoptionMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("adoption_mode"); - - b.Property("CopiedQuestionCount") - .HasColumnType("integer") - .HasColumnName("copied_question_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("GrantId") - .HasColumnType("uuid") - .HasColumnName("grant_id"); - - b.Property("LastSyncedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_synced_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("SourceSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SyncStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("sync_status"); - - b.Property("TargetCollectionId") - .HasColumnType("uuid") - .HasColumnName("target_collection_id"); - - b.Property("TargetEntryId") - .HasColumnType("uuid") - .HasColumnName("target_entry_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_question_bank_adoptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_question_bank_adoptions_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_created_by"); - - b.HasIndex("GrantId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_grant_id"); - - b.HasIndex("SourceQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_source_question_bank_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_updated_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .IsUnique() - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_source_question_ba~"); - - b.HasIndex("TenantId", "TargetCollectionId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_collection_~"); - - b.HasIndex("TenantId", "TargetEntryId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_entry_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_question_ba~"); - - b.HasIndex("TenantId", "Status", "UpdatedAt") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_status_updated_at"); - - b.ToTable("tenant_question_bank_adoptions", null, t => - { - t.HasCheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FavoritedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("favorited_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Note") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.HasKey("Id") - .HasName("pk_user_word_favorites"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_favorites_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_favorites_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_favorites_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_favorites_tenant_id_user_id_word_id"); - - b.ToTable("user_word_favorites", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CorrectStreak") - .HasColumnType("integer") - .HasColumnName("correct_streak"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DueLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("due_level"); - - b.Property("EaseFactor") - .ValueGeneratedOnAdd() - .HasPrecision(4, 2) - .HasColumnType("numeric(4,2)") - .HasDefaultValue(2.50m) - .HasColumnName("ease_factor"); - - b.Property("LastResult") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("last_result"); - - b.Property("LastReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_review_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_review_at"); - - b.Property("ReviewCount") - .HasColumnType("integer") - .HasColumnName("review_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_user_word_progress"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_progress_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_progress_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_progress_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_word_id"); - - b.HasIndex("TenantId", "UserId", "NextReviewAt", "Status", "DueLevel") - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_next_review_at_status_~"); - - b.ToTable("user_word_progress", null, t => - { - t.HasCheckConstraint("ck_user_word_progress_correct_streak", "correct_streak >= 0"); - - t.HasCheckConstraint("ck_user_word_progress_ease_factor", "ease_factor >= 1.30 and ease_factor <= 3.00"); - - t.HasCheckConstraint("ck_user_word_progress_review_count", "review_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsGeneral") - .HasColumnType("boolean") - .HasColumnName("is_general"); - - b.Property("KnowledgeTags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("knowledge_tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ThumbnailUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("thumbnail_url"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("video_url"); - - b.HasKey("Id") - .HasName("pk_video_explanations"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_video_explanations_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_video_explanations_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SourceHash") - .HasDatabaseName("ix_video_explanations_tenant_id_source_hash") - .HasFilter("source_hash is not null"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_video_explanations_tenant_id_subject_id_is_active_sort_order"); - - b.ToTable("video_explanations", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WordCount") - .HasColumnType("integer") - .HasColumnName("word_count"); - - b.HasKey("Id") - .HasName("pk_vocabulary_units"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_units_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_units_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_units_tenant_id_entry_id_content_node_id_region_~"); - - b.ToTable("vocabulary_units", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Example") - .HasColumnType("text") - .HasColumnName("example"); - - b.Property("ExampleTranslation") - .HasColumnType("text") - .HasColumnName("example_translation"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Meaning") - .HasColumnType("text") - .HasColumnName("meaning"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phonetic") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("phonetic"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitId") - .HasColumnType("uuid") - .HasColumnName("unit_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Word") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("word"); - - b.HasKey("Id") - .HasName("pk_vocabulary_words"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_words_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_words_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_words_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UnitId", "IsActive", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_vocabulary_words_tenant_id_unit_id_is_active_sort_order_cre~"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "UnitId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_words_tenant_id_entry_id_content_node_id_unit_id~"); - - b.ToTable("vocabulary_words", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("EffectiveRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("effective_rate"); - - b.Property("GeneratedBy") - .HasColumnType("uuid") - .HasColumnName("generated_by"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidBy") - .HasColumnType("uuid") - .HasColumnName("paid_by"); - - b.Property("PaidUserCount") - .HasColumnType("integer") - .HasColumnName("paid_user_count"); - - b.Property("PaymentAccount") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("payment_account"); - - b.Property("PaymentMethod") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_method"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("SettlementNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("settlement_no"); - - b.Property("SourceCount") - .HasColumnType("integer") - .HasColumnName("source_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlements_tenant_id_id"); - - b.HasIndex("GeneratedBy") - .HasDatabaseName("ix_commission_settlements_generated_by"); - - b.HasIndex("PaidBy") - .HasDatabaseName("ix_commission_settlements_paid_by"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlements_referrer_user_id"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commission_settlements_reviewed_by"); - - b.HasIndex("TenantId", "SettlementNo") - .IsUnique() - .HasDatabaseName("ix_commission_settlements_tenant_id_settlement_no"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart") - .HasDatabaseName("ix_commission_settlements_referrer_start"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart", "PeriodEnd") - .IsUnique() - .HasDatabaseName("ux_commission_settlements_referrer_period"); - - b.ToTable("commission_settlements", null, t => - { - t.HasCheckConstraint("ck_commission_settlements_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_counts", "source_count >= 0 and paid_user_count >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_period", "period_end >= period_start"); - - t.HasCheckConstraint("ck_commission_settlements_rates", "default_rate >= 0 and default_rate <= 1 and (effective_rate is null or (effective_rate >= 0 and effective_rate <= 1))"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AttributionType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("attribution_type"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RateSource") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("rate_source"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("SettlementId") - .HasColumnType("uuid") - .HasColumnName("settlement_id"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_no"); - - b.Property("SourcePaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("source_paid_at"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlement_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlement_items_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlement_items_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_commission_settlement_items_student_user_id"); - - b.HasIndex("TenantId", "SettlementId") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_settlement_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "SourcePaidAt") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_referrer_user_id_sour~"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .IsUnique() - .HasDatabaseName("ix_commission_settlement_items_tenant_id_source_type_source_id"); - - b.ToTable("commission_settlement_items", null, t => - { - t.HasCheckConstraint("ck_commission_settlement_items_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlement_items_rate", "commission_rate >= 0 and commission_rate <= 1"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignmentConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssignmentCursor") - .HasColumnType("integer") - .HasColumnName("assignment_cursor"); - - b.Property("AssignmentMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("AssignmentPool") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_pool") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DelaySeconds") - .HasColumnType("integer") - .HasColumnName("delay_seconds"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("ExamType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("exam_type"); - - b.Property("FormName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("form_name"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Url") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("url"); - - b.HasKey("Id") - .HasName("pk_crm_config"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_config_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_crm_config_tenant_id"); - - b.HasIndex("TenantId", "AssignmentMode") - .HasDatabaseName("ix_crm_config_tenant_id_assignment_mode"); - - b.ToTable("crm_config", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempt") - .HasColumnType("integer") - .HasColumnName("attempt"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("HttpCode") - .HasColumnType("integer") - .HasColumnName("http_code"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Outcome") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("outcome"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("RequestBody") - .HasColumnType("text") - .HasColumnName("request_body"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ResponseSummary") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("response_summary"); - - b.Property("SignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("signed_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_log"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_log_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_log_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_log_tenant_id_record_id_created_at"); - - b.ToTable("crm_webhook_log", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("ScheduledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Source") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("target_url"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_queue"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_queue_tenant_id_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_idempotency_key") - .HasFilter("idempotency_key is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id_created_at") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "Source", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_source_created_at") - .HasFilter("source = 'tenant.student.crm_push'"); - - b.HasIndex("TenantId", "Status", "ScheduledAt", "NextAttemptAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_status_scheduled_at_next_attemp~"); - - b.ToTable("crm_webhook_queue", null, t => - { - t.HasCheckConstraint("ck_crm_webhook_queue_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LandingPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("landing_path"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_codes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_codes_user_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_code"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_user_id"); - - b.HasIndex("TenantId", "Code", "Status") - .HasDatabaseName("ix_referral_codes_tenant_id_code_status"); - - b.ToTable("referral_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("assigned_at"); - - b.Property("AssignedBy") - .HasColumnType("uuid") - .HasColumnName("assigned_by"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("AssignmentMode") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("BindType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bind_type"); - - b.Property("BoundAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("bound_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FirstTrackId") - .HasColumnType("uuid") - .HasColumnName("first_track_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ProtectedUntil") - .HasColumnType("timestamp with time zone") - .HasColumnName("protected_until"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_leads"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_leads_tenant_id_id"); - - b.HasIndex("AssignedBy") - .HasDatabaseName("ix_referral_leads_assigned_by"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_referral_leads_assigned_to_user_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_leads_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_referral_leads_student_user_id"); - - b.HasIndex("TenantId", "FirstTrackId") - .HasDatabaseName("ix_referral_leads_tenant_id_first_track_id"); - - b.HasIndex("TenantId", "StudentUserId") - .IsUnique() - .HasDatabaseName("ix_referral_leads_tenant_id_student_user_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_referrer_user_id_bound_at"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_assigned_to_user_id_status_bound_at"); - - b.ToTable("referral_leads", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Page") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("page"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("QrcodeUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("qrcode_url"); - - b.Property("RefCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("Scene") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("scene"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_qrcodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_qrcodes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_qrcodes_user_id"); - - b.HasIndex("TenantId", "RefCode", "Status") - .HasDatabaseName("ix_referral_qrcodes_tenant_id_ref_code_status"); - - b.HasIndex("TenantId", "Provider", "Scene", "Page") - .IsUnique() - .HasDatabaseName("ix_referral_qrcodes_tenant_id_provider_scene_page"); - - b.ToTable("referral_qrcodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LeaderUserId") - .HasColumnType("uuid") - .HasColumnName("leader_user_id"); - - b.Property("MemberUserId") - .HasColumnType("uuid") - .HasColumnName("member_user_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RelationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("relation_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_team_edges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_team_edges_tenant_id_id"); - - b.HasIndex("LeaderUserId") - .HasDatabaseName("ix_referral_team_edges_leader_user_id"); - - b.HasIndex("MemberUserId") - .HasDatabaseName("ix_referral_team_edges_member_user_id"); - - b.HasIndex("TenantId", "LeaderUserId", "Status") - .HasDatabaseName("ix_referral_team_edges_tenant_id_leader_user_id_status"); - - b.HasIndex("TenantId", "MemberUserId", "RelationType") - .IsUnique() - .HasDatabaseName("ix_referral_team_edges_tenant_id_member_user_id_relation_type"); - - b.ToTable("referral_team_edges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("LeadId") - .HasColumnType("uuid") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("TargetUserId") - .HasColumnType("uuid") - .HasColumnName("target_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_referral_tracks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_tracks_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_tracks_referrer_user_id"); - - b.HasIndex("TargetUserId") - .HasDatabaseName("ix_referral_tracks_target_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_referral_tracks_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "LeadId", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_lead_id_created_at"); - - b.HasIndex("TenantId", "RefCode", "EventType", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_ref_code_event_type_created_at"); - - b.ToTable("referral_tracks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("MinSettlementCents") - .HasColumnType("integer") - .HasColumnName("min_settlement_cents"); - - b.Property("SettlementCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("settlement_cycle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_commission_settings"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_commission_settings_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_commission_settings_tenant_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_commission_settings_updated_by"); - - b.ToTable("tenant_commission_settings", null, t => - { - t.HasCheckConstraint("ck_tenant_commission_settings_default_rate", "default_rate >= 0 and default_rate <= 1"); - - t.HasCheckConstraint("ck_tenant_commission_settings_min_settlement", "min_settlement_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AvatarPreset") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("male") - .HasColumnName("avatar_preset"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastCheckInDate") - .HasColumnType("date") - .HasColumnName("last_check_in_date"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("MasteredWordsCount") - .HasColumnType("integer") - .HasColumnName("mastered_words_count"); - - b.Property("ModuleSelections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_selections") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Progress") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("progress") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionsAnsweredToday") - .HasColumnType("integer") - .HasColumnName("questions_answered_today"); - - b.Property("RecentActivities") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("recent_activities") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SelectedMajorId") - .HasColumnType("uuid") - .HasColumnName("selected_major_id"); - - b.Property("SelectedSchoolId") - .HasColumnType("uuid") - .HasColumnName("selected_school_id"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_student_profiles"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_student_profiles_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_student_profiles_user_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_student_profiles_tenant_id_region_id"); - - b.HasIndex("TenantId", "SelectedMajorId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_major_id"); - - b.HasIndex("TenantId", "SelectedSchoolId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_school_id"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_student_profiles_tenant_id_user_id"); - - b.ToTable("student_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AvatarUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("avatar_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("LastSeenAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPasswordHash") - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasColumnName("legacy_password_hash"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PasswordMigrationRequired") - .HasColumnType("boolean") - .HasColumnName("password_migration_required"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("PrimaryRole") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("primary_role"); - - b.Property("RawProfile") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_profile") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Score") - .HasColumnType("integer") - .HasColumnName("score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Username") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Email") - .IsUnique() - .HasDatabaseName("ix_users_email"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_users_legacy_id"); - - b.HasIndex("Phone") - .IsUnique() - .HasDatabaseName("ix_users_phone"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("OpenId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("open_id"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderSubject") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("provider_subject"); - - b.Property("SecretPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("secret_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UnionId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("union_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_identities"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_identities_user_id"); - - b.HasIndex("Provider", "ProviderSubject") - .IsUnique() - .HasDatabaseName("ix_user_identities_provider_provider_subject"); - - b.ToTable("user_identities", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("IssueCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("RawValueSample") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("raw_value_sample"); - - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_run_id"); - - b.HasIndex("RunId", "Severity", "CollectionName") - .HasDatabaseName("ix_pb_import_issues_run_id_severity_collection_name"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("SourceKind") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_kind"); - - b.Property("SourceName") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_runs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_runs_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "StartedAt") - .HasDatabaseName("ix_pb_import_runs_tenant_id_status_started_at"); - - b.ToTable("pb_import_runs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("CollectionName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Errors") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("errors") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ImportedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("imported_at") - .HasDefaultValueSql("now()"); - - b.Property("Normalized") - .HasColumnType("boolean") - .HasColumnName("normalized"); - - b.Property("Record") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("record") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("RunId", "CollectionName", "LegacyId") - .HasName("pk_pb_raw_records"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_run_id"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_raw_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("AnsweredAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("answered_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsCorrect") - .HasColumnType("boolean") - .HasColumnName("is_correct"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("QuestionVersionId") - .HasColumnType("uuid") - .HasColumnName("question_version_id"); - - b.Property("SelectedOptions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("selected_options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_answer_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_answer_records_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_answer_records_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_answer_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id"); - - b.HasIndex("TenantId", "QuestionId", "QuestionVersionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id_question_version_id"); - - b.HasIndex("TenantId", "UserId", "PracticeSessionId") - .HasDatabaseName("ix_answer_records_tenant_id_user_id_practice_session_id"); - - b.ToTable("answer_records", null, t => - { - t.HasCheckConstraint("ck_answer_records_version_requires_question", "question_version_id is null or question_id is not null"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActiveUsers") - .HasColumnType("integer") - .HasColumnName("active_users"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("NewOrders") - .HasColumnType("integer") - .HasColumnName("new_orders"); - - b.Property("NewQuestions") - .HasColumnType("integer") - .HasColumnName("new_questions"); - - b.Property("NewRevenueCents") - .HasColumnType("integer") - .HasColumnName("new_revenue_cents"); - - b.Property("NewUsers") - .HasColumnType("integer") - .HasColumnName("new_users"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_dashboard_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_dashboard_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_stat_date_region_id"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId"), false); - - b.ToTable("dashboard_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("ExamAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("exam_at"); - - b.Property("ExamName") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("exam_name"); - - b.Property("ExamType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("exam_type"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_exam_dates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_exam_dates_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_exam_dates_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_exam_dates_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId", "ExamAt") - .HasDatabaseName("ix_exam_dates_tenant_id_school_id_exam_at"); - - b.ToTable("exam_dates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_favorite_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_favorite_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_favorite_questions_tenant_id_question_id"); - - b.ToTable("favorite_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_mode"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementId") - .HasColumnType("uuid") - .HasColumnName("entitlement_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("GrantedCount") - .HasColumnType("integer") - .HasColumnName("granted_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("Reason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("reason"); - - b.Property("RequestedCount") - .HasColumnType("integer") - .HasColumnName("requested_count"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_access_events_user_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_access_events_tenant_id_practice_session_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_practice_access_events_tenant_id_user_id_created_at"); - - b.ToTable("practice_access_events", null, t => - { - t.HasCheckConstraint("ck_practice_access_events_consumed_free_quota", "consumed_free_quota >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_granted_count", "granted_count >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_requested_count", "requested_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FreeLimit") - .HasColumnType("integer") - .HasColumnName("free_limit"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsageDate") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasColumnName("usage_date") - .HasDefaultValueSql("current_date"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_daily_usage"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_daily_usage_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_daily_usage_user_id"); - - b.HasIndex("TenantId", "UserId", "UsageDate") - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date"); - - b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId") - .IsUnique() - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date_scope_typ~"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId"), false); - - b.ToTable("practice_daily_usage", null, t => - { - t.HasCheckConstraint("ck_practice_daily_usage_free_limit", "free_limit >= 0"); - - t.HasCheckConstraint("ck_practice_daily_usage_used_count", "used_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessEntitlementId") - .HasColumnType("uuid") - .HasColumnName("access_entitlement_id"); - - b.Property("AccessMode") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("free") - .HasColumnName("access_mode"); - - b.Property("AccessSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_sessions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "UserId", "Id") - .HasName("ak_practice_sessions_tenant_id_user_id_id"); - - 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"); - - b.ToTable("practice_sessions", null, t => - { - t.HasCheckConstraint("ck_practice_sessions_consumed_free_quota", "consumed_free_quota >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionResults") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_results") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionStats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("section_stats") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("SubmittedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("submitted_at") - .HasDefaultValueSql("now()"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalQuestions") - .HasColumnType("integer") - .HasColumnName("total_questions"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.Property("WrongQuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("wrong_question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.HasKey("Id") - .HasName("pk_practice_session_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_reports_tenant_id_id"); - - 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.ToTable("practice_session_reports", null, t => - { - t.HasCheckConstraint("ck_practice_session_reports_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_reports_counts", "total_questions >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_duration", "duration_seconds >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("question_type"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SectionName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("section_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_practice_session_report_sections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_report_sections_tenant_id_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_practice_session~"); - - b.HasIndex("TenantId", "ReportId", "SectionKey") - .IsUnique() - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sectio~"); - - b.HasIndex("TenantId", "ReportId", "SortOrder") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sort_o~"); - - b.ToTable("practice_session_report_sections", null, t => - { - t.HasCheckConstraint("ck_practice_session_report_sections_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_report_sections_counts", "question_count >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_report_sections_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastAccessAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_access_at"); - - b.Property("LastPracticeAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_practice_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("practice_type"); - - b.Property("Progress") - .HasColumnType("integer") - .HasColumnName("progress"); - - b.Property("TargetLegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("target_legacy_id"); - - b.Property("TargetName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("target_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_recent_practices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_recent_practices_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_recent_practices_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_recent_practices_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "LastAccessAt") - .HasDatabaseName("ix_recent_practices_tenant_id_user_id_last_access_at"); - - b.ToTable("recent_practices", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attachments") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("attachments") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("Contact") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("contact"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("HandledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("handled_at"); - - b.Property("HandledBy") - .HasColumnType("uuid") - .HasColumnName("handled_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Resolution") - .HasColumnType("text") - .HasColumnName("resolution"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_reports_tenant_id_id"); - - b.HasIndex("HandledBy") - .HasDatabaseName("ix_reports_handled_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_reports_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_reports_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_question_id_created_at") - .HasFilter("question_id is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_status_created_at"); - - b.ToTable("reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_report_status_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_report_status_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_report_status_events_actor_user_id"); - - b.HasIndex("TenantId", "ReportId", "CreatedAt") - .HasDatabaseName("ix_report_status_events_tenant_id_report_id_created_at"); - - b.ToTable("report_status_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CodeCount") - .HasColumnType("integer") - .HasColumnName("code_count"); - - b.Property("CodeEstimated") - .HasColumnType("integer") - .HasColumnName("code_estimated"); - - b.Property("CodeUsed") - .HasColumnType("integer") - .HasColumnName("code_used"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EstimatedRevenueCents") - .HasColumnType("integer") - .HasColumnName("estimated_revenue_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("OrderCount") - .HasColumnType("integer") - .HasColumnName("order_count"); - - b.Property("RealRevenueCents") - .HasColumnType("integer") - .HasColumnName("real_revenue_cents"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_revenue_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_revenue_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_stat_date_region_id_sale_type"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType"), false); - - b.ToTable("revenue_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceAfter") - .HasColumnType("integer") - .HasColumnName("balance_after"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Points") - .HasColumnType("integer") - .HasColumnName("points"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_score_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_score_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_score_events_user_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_user_score_events_tenant_id_idempotency_key"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_user_score_events_tenant_id_user_id_created_at"); - - b.ToTable("user_score_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("LastWrongAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_wrong_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("WrongCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(1) - .HasColumnName("wrong_count"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_wrong_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_wrong_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_wrong_questions_tenant_id_question_id"); - - b.ToTable("wrong_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_announcements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_announcements_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_announcements_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "IsActive", "SortOrder") - .HasDatabaseName("ix_announcements_tenant_id_is_active_sort_order"); - - b.ToTable("announcements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_audit_logs"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_audit_logs_actor_user_id"); - - b.HasIndex("TenantId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_created_at"); - - b.HasIndex("TenantId", "TargetType", "TargetId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_target_type_target_id_created_at"); - - b.ToTable("audit_logs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("ConditionExtra") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("condition_extra") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ConditionField") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("condition_field"); - - b.Property("ConditionOperator") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("condition_operator"); - - b.Property("ConditionValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("condition_value"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon_url"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Level") - .HasColumnType("integer") - .HasColumnName("level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnlockType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("unlock_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_badges_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsActive", "SortOrder") - .HasDatabaseName("ix_badges_tenant_id_category_is_active_sort_order"); - - b.ToTable("badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("BorderColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("border_color"); - - b.Property("ButtonLink") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("button_link"); - - b.Property("ButtonText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("button_text"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Subtitle") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("subtitle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_banners"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_banners_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_banners_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_banners_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("banners", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Answer") - .HasColumnType("text") - .HasColumnName("answer"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Question") - .HasColumnType("text") - .HasColumnName("question"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_faqs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_faqs_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_faqs_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_faqs_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("faqs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("AdoptionId") - .HasColumnType("uuid") - .HasColumnName("adoption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("ReadBy") - .HasColumnType("uuid") - .HasColumnName("read_by"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_content_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_content_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_content_notifications_created_by"); - - b.HasIndex("ReadBy") - .HasDatabaseName("ix_tenant_content_notifications_read_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_source_question_bank~"); - - b.HasIndex("TenantId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_notification_type_de~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "AdoptionId", "NotificationType", "Status") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_adoption_id_notifica~"); - - b.ToTable("tenant_content_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActivePublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ActiveTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("active_template_code"); - - b.Property("ActiveTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DraftPublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("draft_template_code"); - - b.Property("DraftTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftUpdatedBy") - .HasColumnType("uuid") - .HasColumnName("draft_updated_by"); - - b.Property("PublishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("published_at"); - - b.Property("PublishedBy") - .HasColumnType("uuid") - .HasColumnName("published_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_configs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_theme_configs_tenant_id_id"); - - b.HasIndex("ActiveTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_active_template_code"); - - b.HasIndex("DraftTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_draft_template_code"); - - b.HasIndex("DraftUpdatedBy") - .HasDatabaseName("ix_tenant_theme_configs_draft_updated_by"); - - b.HasIndex("PublishedBy") - .HasDatabaseName("ix_tenant_theme_configs_published_by"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_configs_tenant_id"); - - b.ToTable("tenant_theme_configs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PreviewImageUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_image_url"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_templates"); - - b.HasAlternateKey("Code") - .HasName("ak_tenant_theme_templates_code"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_templates_code"); - - b.HasIndex("Status", "SortOrder", "Code") - .HasDatabaseName("ix_tenant_theme_templates_status_sort_order_code"); - - b.ToTable("tenant_theme_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BadgeId") - .HasColumnType("uuid") - .HasColumnName("badge_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrantedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("granted_at"); - - b.Property("GrantedBy") - .HasColumnType("uuid") - .HasColumnName("granted_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_badges_tenant_id_id"); - - b.HasIndex("GrantedBy") - .HasDatabaseName("ix_user_badges_granted_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_badges_user_id"); - - b.HasIndex("TenantId", "BadgeId") - .HasDatabaseName("ix_user_badges_tenant_id_badge_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "BadgeId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_user_id_badge_id"); - - b.ToTable("user_badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_user_notifications_created_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_notifications_user_id"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .HasDatabaseName("ix_user_notifications_tenant_id_source_type_source_id"); - - b.HasIndex("TenantId", "UserId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_notification_type_dedu~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "UserId", "Status", "CreatedAt") - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_status_created_at"); - - b.ToTable("user_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("AcknowledgedBy") - .HasColumnType("uuid") - .HasColumnName("acknowledged_by"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("AuditLogId") - .HasColumnType("uuid") - .HasColumnName("audit_log_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FirstSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("first_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("LastSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("RuleId") - .HasColumnType("uuid") - .HasColumnName("rule_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alerts"); - - b.HasIndex("AcknowledgedBy") - .HasDatabaseName("ix_platform_audit_alerts_acknowledged_by"); - - b.HasIndex("AuditLogId") - .HasDatabaseName("ix_platform_audit_alerts_audit_log_id"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_platform_audit_alerts_resolved_by"); - - b.HasIndex("RuleId", "AuditLogId") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alerts_rule_id_audit_log_id"); - - b.HasIndex("Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_status_severity_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_tenant_id_status_created_at"); - - b.ToTable("platform_audit_alerts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("ActionPatterns") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("action_patterns") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("Conditions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("conditions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.PrimitiveCollection("TargetTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("target_types") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alert_rules"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alert_rules_code"); - - b.HasIndex("TenantId") - .HasDatabaseName("ix_platform_audit_alert_rules_tenant_id"); - - b.HasIndex("Enabled", "Severity", "Code") - .HasDatabaseName("ix_platform_audit_alert_rules_enabled_severity_code"); - - b.ToTable("platform_audit_alert_rules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationChannel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChannelCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel_code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MinReminderLevel") - .HasColumnType("integer") - .HasColumnName("min_reminder_level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.PrimitiveCollection("ReminderChannels") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_channels") - .HasDefaultValueSql("array['internal']::text[]"); - - b.PrimitiveCollection("ReminderTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_types") - .HasDefaultValueSql("array['overdue', 'final_notice']::text[]"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.PrimitiveCollection("TenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WebhookUrl") - .IsRequired() - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("webhook_url"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_channels"); - - b.HasIndex("ChannelCode") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_channels_channel_code"); - - b.HasIndex("Enabled", "MinReminderLevel", "ChannelCode") - .HasDatabaseName("ix_platform_dunning_notification_channels_enabled_min_reminder~"); - - b.ToTable("platform_dunning_notification_channels", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_channels_level", "min_reminder_level between 1 and 20"); - - t.HasCheckConstraint("ck_platform_dunning_channels_timeout", "timeout_seconds between 1 and 60"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("ChannelId") - .HasColumnType("uuid") - .HasColumnName("channel_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.Property("ReminderId") - .HasColumnType("uuid") - .HasColumnName("reminder_id"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScheduledAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at") - .HasDefaultValueSql("now()"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_platform_dunning_notification_events_tenant_id_id"); - - b.HasIndex("ChannelId", "ReminderId") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_events_channel_id_reminder_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_invoice_id"); - - b.HasIndex("TenantId", "ReminderId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_reminder_id"); - - b.HasIndex("InvoiceId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_invoice_id_status_crea~"); - - b.HasIndex("ReminderId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_reminder_id_status_cre~"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_status_creat~"); - - b.HasIndex("Status", "NextAttemptAt", "ScheduledAt", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_status_next_attempt_at~"); - - b.ToTable("platform_dunning_notification_events", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_events_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformSaasPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseAmountCents") - .HasColumnType("integer") - .HasColumnName("base_amount_cents"); - - b.Property("BillingCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_cycle"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IncludedQuotas") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("included_quotas") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OveragePrices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("overage_prices") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_saas_plans"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_saas_plans_code"); - - b.HasIndex("Status", "SortOrder") - .HasDatabaseName("ix_platform_saas_plans_status_sort_order"); - - b.ToTable("platform_saas_plans", null, t => - { - t.HasCheckConstraint("ck_platform_saas_plans_amount", "base_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BankAccountMasked") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("bank_account_masked"); - - b.Property("BankName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bank_name"); - - b.Property("BillingAddress") - .HasColumnType("text") - .HasColumnName("billing_address"); - - b.Property("BillingName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("billing_name"); - - b.Property("ContactEmail") - .HasMaxLength(300) - .HasColumnType("citext") - .HasColumnName("contact_email"); - - b.Property("ContactName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("contact_name"); - - b.Property("ContactPhone") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("contact_phone"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceTitle") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("invoice_title"); - - b.Property("InvoiceType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TaxId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("tax_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_billing_profiles"); - - b.ToTable("tenant_billing_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceCents") - .HasColumnType("integer") - .HasColumnName("balance_cents"); - - b.Property("BillingPeriodEnd") - .HasColumnType("date") - .HasColumnName("billing_period_end"); - - b.Property("BillingPeriodStart") - .HasColumnType("date") - .HasColumnName("billing_period_start"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("DiscountCents") - .HasColumnType("integer") - .HasColumnName("discount_cents"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("invoice_no"); - - b.Property("InvoiceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidCents") - .HasColumnType("integer") - .HasColumnName("paid_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubtotalCents") - .HasColumnType("integer") - .HasColumnName("subtotal_cents"); - - b.Property("TaxCents") - .HasColumnType("integer") - .HasColumnName("tax_cents"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCents") - .HasColumnType("integer") - .HasColumnName("total_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoices_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoices_created_by"); - - b.HasIndex("InvoiceNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_invoice_no"); - - b.HasIndex("TenantId", "BillingPeriodStart", "BillingPeriodEnd") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_tenant_id_billing_period_start_billing_peri~") - .HasFilter("invoice_type = 'usage_overage' and status <> 'void' and metadata->>'source' = 'usage_overage_auto'"); - - b.HasIndex("TenantId", "Status", "DueDate") - .HasDatabaseName("ix_tenant_invoices_tenant_id_status_due_date"); - - b.ToTable("tenant_invoices", null, t => - { - t.HasCheckConstraint("ck_tenant_invoices_amounts", "subtotal_cents >= 0 and discount_cents >= 0 and tax_cents >= 0 and total_cents >= 0 and paid_cents >= 0 and balance_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoices_period", "billing_period_end is null or billing_period_start is null or billing_period_end >= billing_period_start"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("description"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("ItemRefId") - .HasColumnType("uuid") - .HasColumnName("item_ref_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Quantity") - .HasPrecision(12, 2) - .HasColumnType("numeric(12,2)") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_items_tenant_id_id"); - - b.HasIndex("InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_invoice_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_items", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_items_amounts", "unit_amount_cents >= 0 and amount_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_no"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReceivedBy") - .HasColumnType("uuid") - .HasColumnName("received_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_payments_tenant_id_id"); - - b.HasIndex("PaymentNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_payments_payment_no"); - - b.HasIndex("ReceivedBy") - .HasDatabaseName("ix_tenant_invoice_payments_received_by"); - - b.HasIndex("InvoiceId", "Status") - .HasDatabaseName("ix_tenant_invoice_payments_invoice_id_status"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_payments_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_payments", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("BalanceCentsSnapshot") - .HasColumnType("integer") - .HasColumnName("balance_cents_snapshot"); - - b.Property("Channel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("channel"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Message") - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReminderDate") - .HasColumnType("date") - .HasColumnName("reminder_date"); - - b.Property("ReminderLevel") - .HasColumnType("integer") - .HasColumnName("reminder_level"); - - b.Property("ReminderType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("reminder_type"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_reminders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_reminders_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoice_reminders_created_by"); - - b.HasIndex("InvoiceId", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_invoice_id_reminder_date"); - - b.HasIndex("TenantId", "Status", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_status_reminder_date"); - - b.HasIndex("TenantId", "InvoiceId", "ReminderType", "Channel", "ReminderDate") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_invoice_id_reminder_type~"); - - b.ToTable("tenant_invoice_reminders", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_reminders_balance", "balance_cents_snapshot >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_reminders_level", "reminder_level between 1 and 20"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CurrentVersionId") - .HasColumnType("uuid") - .HasColumnName("current_version_id"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExamMarkers") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("exam_markers") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("HasVideoExplanation") - .HasColumnType("boolean") - .HasColumnName("has_video_explanation"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyNodeId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_node_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("MediaUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("media_url"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PrimaryCollectionId") - .HasColumnType("uuid") - .HasColumnName("primary_collection_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("TypeLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("type_label"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_questions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_questions_tenant_id_id"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_questions_tenant_id_category_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_questions_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_questions_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_questions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_questions_tenant_id_node_id"); - - b.HasIndex("TenantId", "PrimaryCollectionId") - .HasDatabaseName("ix_questions_tenant_id_primary_collection_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_questions_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_questions_tenant_id_subject_id"); - - b.HasIndex("TenantId", "Id", "CurrentVersionId") - .HasDatabaseName("ix_questions_tenant_id_id_current_version_id"); - - b.ToTable("questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SourceScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_scope"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_banks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_banks_tenant_id_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_banks_tenant_id_region_id"); - - b.ToTable("question_banks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("CodeLang") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code_lang"); - - b.Property("CodeTemplate") - .HasColumnType("text") - .HasColumnName("code_template"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CorrectOptionIndex") - .HasColumnType("integer") - .HasColumnName("correct_option_index"); - - b.Property("CorrectOptionIndices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("correct_option_indices") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Explanation") - .HasColumnType("text") - .HasColumnName("explanation"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubQuestions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sub_questions") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("VersionNo") - .HasColumnType("integer") - .HasColumnName("version_no"); - - b.HasKey("Id") - .HasName("pk_question_versions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_versions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "QuestionId", "Id") - .HasName("ak_question_versions_tenant_id_question_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_versions_created_by"); - - b.HasIndex("QuestionId", "VersionNo") - .IsUnique() - .HasDatabaseName("ix_question_versions_question_id_version_no"); - - b.ToTable("question_versions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FailureCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("failure_code"); - - b.Property("Identifier") - .HasMaxLength(320) - .HasColumnType("character varying(320)") - .HasColumnName("identifier"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_login_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_login_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_login_events_user_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_auth_login_events_tenant_id_user_id_created_at"); - - b.ToTable("auth_login_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TokenHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("token_hash"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_sessions_tenant_id_id"); - - b.HasIndex("TokenHash") - .IsUnique() - .HasDatabaseName("ix_auth_sessions_token_hash"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_sessions_user_id"); - - b.HasIndex("TenantId", "UserId", "ExpiresAt") - .HasDatabaseName("ix_auth_sessions_tenant_id_user_id_expires_at") - .HasFilter("revoked_at is null"); - - b.ToTable("auth_sessions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Dimension") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("dimension"); - - b.Property("ScopeHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("scope_hash"); - - b.Property("BucketStart") - .HasColumnType("timestamp with time zone") - .HasColumnName("bucket_start"); - - b.Property("RequestCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(0) - .HasColumnName("request_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId", "Dimension", "ScopeHash", "BucketStart") - .HasName("pk_sms_send_rate_limits"); - - b.HasIndex("UpdatedAt") - .HasDatabaseName("ix_sms_send_rate_limits_updated_at"); - - b.ToTable("sms_send_rate_limits", null, t => - { - t.HasCheckConstraint("ck_sms_send_rate_limits_request_count", "request_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CodeHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("code_hash"); - - b.Property("ConsumedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("consumed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phone") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Purpose") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("purpose"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_sms_verification_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_sms_verification_codes_tenant_id_id"); - - b.HasIndex("TenantId", "Phone", "Purpose") - .IsUnique() - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "CreatedAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_created_at") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "ExpiresAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_expires_at"); - - b.ToTable("sms_verification_codes", null, t => - { - t.HasCheckConstraint("ck_sms_verification_codes_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BillingStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_status"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegalName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("legal_name"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OwnerUserId") - .HasColumnType("uuid") - .HasColumnName("owner_user_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("slug"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenants"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenants_legacy_id"); - - b.HasIndex("OwnerUserId") - .HasDatabaseName("ix_tenants_owner_user_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_tenants_slug"); - - b.ToTable("tenants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_auth_providers"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_auth_providers_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider"); - - b.HasIndex("TenantId", "Provider", "Status") - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider_status"); - - b.ToTable("tenant_auth_providers", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BrandName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("brand_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FaviconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("favicon_url"); - - b.Property("LogoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("logo_url"); - - b.Property("OrganizationName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("organization_name"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ServiceAccountName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("service_account_name"); - - b.Property("ServiceWechat") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("service_wechat"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("Slogan") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("slogan"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_branding"); - - b.ToTable("tenant_branding", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_classes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_classes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_classes_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_classes_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_code") - .HasFilter("code is not null and code <> ''"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_tenant_classes_tenant_id_region_id") - .HasFilter("region_id is not null"); - - b.HasIndex("TenantId", "Status", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_tenant_classes_tenant_id_status_sort_order_created_at"); - - b.ToTable("tenant_classes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("JoinedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("joined_at") - .HasDefaultValueSql("now()"); - - b.Property("LeftAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("left_at"); - - b.Property("MemberType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("member_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_class_members"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_class_members_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_class_members_user_id"); - - b.HasIndex("TenantId", "ClassId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_status_member_type"); - - b.HasIndex("TenantId", "ClassId", "UserId", "MemberType") - .IsUnique() - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_user_id_member_type"); - - b.HasIndex("TenantId", "UserId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_user_id_status_member_type"); - - b.ToTable("tenant_class_members", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DomainType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("domain_type"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(253) - .HasColumnType("citext") - .HasColumnName("host"); - - b.Property("IsPrimary") - .HasColumnType("boolean") - .HasColumnName("is_primary"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VerificationToken") - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("verification_token"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.HasKey("Id") - .HasName("pk_tenant_domains"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_domains_tenant_id_id"); - - b.HasIndex("Host") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_host"); - - b.HasIndex("TenantId", "IsPrimary") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_tenant_id_is_primary") - .HasFilter("is_primary"); - - b.ToTable("tenant_domains", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyRole") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("legacy_role"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Role") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("role"); - - b.Property("RoleTemplateId") - .HasColumnType("uuid") - .HasColumnName("role_template_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_memberships"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_memberships_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_memberships_user_id"); - - b.HasIndex("TenantId", "RoleTemplateId") - .HasDatabaseName("ix_tenant_memberships_tenant_id_role_template_id"); - - b.HasIndex("TenantId", "UserId", "Role") - .IsUnique() - .HasDatabaseName("ix_tenant_memberships_tenant_id_user_id_role"); - - b.ToTable("tenant_memberships", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("base_role"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DataScope") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("data_scope") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FieldPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IsSystem") - .HasColumnType("boolean") - .HasColumnName("is_system"); - - b.Property("MenuPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("menu_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModulePermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_role_templates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_role_templates_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_role_templates_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_role_templates_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_role_templates_tenant_id_code"); - - b.HasIndex("TenantId", "Status", "SortOrder") - .HasDatabaseName("ix_tenant_role_templates_tenant_id_status_sort_order"); - - b.ToTable("tenant_role_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Purpose") - .IsRequired() - .HasMaxLength(80) - .HasColumnType("character varying(80)") - .HasColumnName("purpose"); - - b.Property("RotatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rotated_at"); - - b.Property("SecretKey") - .IsRequired() - .HasMaxLength(120) - .HasColumnType("character varying(120)") - .HasColumnName("secret_key"); - - b.Property("SecretPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("secret_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecretRef") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_secrets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_secrets_tenant_id_id"); - - b.HasIndex("TenantId", "SecretRef") - .IsUnique() - .HasDatabaseName("ix_tenant_secrets_tenant_id_secret_ref"); - - b.HasIndex("TenantId", "Purpose", "Provider", "SecretKey") - .IsUnique() - .HasDatabaseName("ix_tenant_secrets_tenant_id_purpose_provider_secret_key"); - - b.HasIndex("TenantId", "Purpose", "Provider", "Status") - .HasDatabaseName("ix_tenant_secrets_tenant_id_purpose_provider_status"); - - b.ToTable("tenant_secrets", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("AdminFeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("admin_feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PublicConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_settings"); - - b.ToTable("tenant_settings", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CompletedBy") - .HasColumnType("uuid") - .HasColumnName("completed_by"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("FollowupType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("followup_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_student_followups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_followups_tenant_id_id"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_tenant_student_followups_assigned_to_user_id"); - - b.HasIndex("CompletedBy") - .HasDatabaseName("ix_tenant_student_followups_completed_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_followups_student_user_id"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_assigned_to_user_id_stat~") - .HasFilter("assigned_to_user_id is not null"); - - b.HasIndex("TenantId", "ClassId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_class_id_status_due_at") - .HasFilter("class_id is not null"); - - b.HasIndex("TenantId", "StudentUserId", "Status", "DueAt", "CreatedAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_student_user_id_status_d~"); - - b.ToTable("tenant_student_followups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("IsPinned") - .HasColumnType("boolean") - .HasColumnName("is_pinned"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NoteType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("note_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_tenant_student_notes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_notes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_student_notes_created_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_notes_student_user_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_student_notes_updated_by"); - - b.HasIndex("TenantId", "CreatedBy", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_created_by_created_at") - .HasFilter("created_by is not null"); - - b.HasIndex("TenantId", "StudentUserId", "IsPinned", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_student_user_id_is_pinned_cr~"); - - b.ToTable("tenant_student_notes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_categories_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_majors_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_module_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_module_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_regions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_region_modules_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_region_modules_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_schools_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineField", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_scoreline_fields_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_fields_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_scoreline_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AgentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_agent_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_activation_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UsedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_used_by"); - - b.HasOne("Tiku.Domain.Commerce.CodeBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_code_batches_tenant_id_batch_id"); - - b.HasOne("Tiku.Domain.Commerce.CouponRedemption", null) - .WithMany() - .HasForeignKey("TenantId", "CouponRedemptionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_coupon_redemptions_tenant_id_coupon_redemp~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "UsedRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_regions_tenant_id_used_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_code_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_batches_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedTo") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_assigned_to"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_resolved_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_batc~"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_item~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_refund_requests_ten~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issue_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationIssue", null) - .WithMany() - .HasForeignKey("TenantId", "IssueId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_commerce_reconciliatio~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_commerce_reconciliation_batch~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_commerce_refund_requests_tena~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_commerce_refund_requests_tenant_id_r~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ProcessedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_processed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RequestedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_requested_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_refund_requests_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupons_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_coupons_svip_plans_tenant_id_plan_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupon_redemptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.Coupon", null) - .WithMany() - .HasForeignKey("TenantId", "CouponId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_coupons_tenant_id_coupon_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RevokedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_entitlements_users_revoked_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_orders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_orders_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payment_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_payment_events_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_products_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_products_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_svip_plans_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_svip_plans_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_payment_accounts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_subscriptions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_usage_records_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_ai_recommendation_reports_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_app_assets_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_assets_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("VerifiedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_verified_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_content_nodes_tenant_id_content_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_asset_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_access_events_content_assets_tenant_id_asset_~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_security_scan_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_security_scan_events_content_assets_tenant_id~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_entries_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_import_issues_content_import_items_tenant_id_item_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_import_jobs_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_jobs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_categories_tenant_id_target_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "TargetContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_content_nodes_tenant_id_target_content_~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_question_banks_tenant_id_target_questio~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "TargetRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_regions_tenant_id_target_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_nodes_content_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_chapters_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_chapters_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_handbook_chapters_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.HandbookSubject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_chapters_handbook_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.HandbookChapter", null) - .WithMany() - .HasForeignKey("TenantId", "ChapterId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_entries_handbook_chapters_tenant_id_chapter_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_entries_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_handbook_entries_content_entries_tenant_id_entry_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_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_handbook_subjects_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_images_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_blueprints_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_question_collections_tenant_id_collecti~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_bank_grants_question_banks_source_question_bank_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_question_banks_tenant_id_question_bank~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_question_collections_tenant_id_co~"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_type_groups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_type_groups_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_videos_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.Content.VideoExplanation", null) - .WithMany() - .HasForeignKey("TenantId", "VideoId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_video_explanations_tenant_id_video_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SourceSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_source_subject_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_created_by"); - - b.HasOne("Tiku.Domain.Content.QuestionBankGrant", null) - .WithMany() - .HasForeignKey("GrantId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_bank_grants_grant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_source_questi~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_collections_tenant_~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "TargetEntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_content_entries_tenant_id_ta~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_tenant_id_tar~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_video_explanations_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_video_explanations_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_units_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_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_vocabulary_units_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_words_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_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_vocabulary_words_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyUnit", null) - .WithMany() - .HasForeignKey("TenantId", "UnitId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_vocabulary_units_tenant_id_unit_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GeneratedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_generated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PaidBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_paid_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.CommissionSettlement", null) - .WithMany() - .HasForeignKey("TenantId", "SettlementId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_commission_settlements_tenant_i~"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_config_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_log_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_queue_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralTrack", null) - .WithMany() - .HasForeignKey("TenantId", "FirstTrackId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_referral_tracks_tenant_id_first_track_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_qrcodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_qrcodes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("LeaderUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_team_edges_users_leader_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("MemberUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_users_member_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("TargetUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_target_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_tracks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralLead", null) - .WithMany() - .HasForeignKey("TenantId", "LeadId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_referral_leads_tenant_id_lead_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_commission_settings_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_commission_settings_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedMajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_majors_tenant_id_selected_major_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedSchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_schools_tenant_id_selected_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_identities_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_pb_import_issues_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_runs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId", "QuestionVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_question_versions_tenant_id_question_id_ques~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "UserId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "UserId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_practice_sessions_tenant_id_user_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_dashboard_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_dashboard_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_exam_dates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_practice_sessions_tenant_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .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.PracticeSessionReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .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") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_practice_sessions_tenant_id_practi~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_sessions_tenant_i~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSessionReport", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_session_reports_t~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_recent_practices_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_recent_practices_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("HandledBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_handled_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_reports_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_report_status_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.Report", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_reports_tenant_id_report_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_revenue_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_revenue_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_announcements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_audit_logs_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_audit_logs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_badges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_banners_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_banners_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_faqs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_faqs_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReadBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_read_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_content_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "SourceQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_tenant_content_notifications_question_banks_tenant_id_sourc~"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("ActiveTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_active_template~"); - - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("DraftTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_draft_template_~"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("DraftUpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_draft_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PublishedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_published_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_theme_configs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GrantedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_badges_users_granted_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_badges_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_users_user_id"); - - b.HasOne("Tiku.Domain.Operations.Badge", null) - .WithMany() - .HasForeignKey("TenantId", "BadgeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_badges_tenant_id_badge_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AcknowledgedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_acknowledged_by"); - - b.HasOne("Tiku.Domain.Operations.AuditLog", null) - .WithMany() - .HasForeignKey("AuditLogId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_audit_logs_audit_log_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_resolved_by"); - - b.HasOne("Tiku.Domain.Platform.PlatformAuditAlertRule", null) - .WithMany() - .HasForeignKey("RuleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_platform_audit_alert_rules_rule_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alerts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alert_rules_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.HasOne("Tiku.Domain.Platform.PlatformDunningNotificationChannel", null) - .WithMany() - .HasForeignKey("ChannelId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_platform_dunning_notif~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoices_tenant~"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoiceReminder", null) - .WithMany() - .HasForeignKey("TenantId", "ReminderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoice_reminde~"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Platform.TenantBillingProfile", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_billing_profiles_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoices_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoices_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReceivedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_payments_users_received_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_reminders_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenant_invoices_tenant_id_invoice_~"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_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_questions_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "PrimaryCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_collections_tenant_id_primary_collection~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_banks_tenant_id_question_bank_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_subjects_tenant_id_subject_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "Id", "CurrentVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_versions_tenant_id_id_current_version_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_banks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_banks_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_versions_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_versions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_login_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_auth_login_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_send_rate_limits_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_verification_codes_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("OwnerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenants_users_owner_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_auth_providers_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantBranding", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_branding_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_classes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_classes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_domains_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantRoleTemplate", null) - .WithMany() - .HasForeignKey("TenantId", "RoleTemplateId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_role_templates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSecret", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_secrets_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantSettings", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_settings_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CompletedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_completed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_updated_by"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726112706_AddPaymentSdkAndTenantSecretFoundation.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726112706_AddPaymentSdkAndTenantSecretFoundation.cs deleted file mode 100644 index 6b102df..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726112706_AddPaymentSdkAndTenantSecretFoundation.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Text.Json; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - /// - public partial class AddPaymentSdkAndTenantSecretFoundation : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "tenant_secrets", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - purpose = table.Column(type: "character varying(80)", maxLength: 80, nullable: false), - provider = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), - secret_key = table.Column(type: "character varying(120)", maxLength: 120, nullable: false), - secret_ref = table.Column(type: "character varying(300)", maxLength: 300, nullable: false), - status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - secret_payload = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - rotated_at = table.Column(type: "timestamp with time zone", nullable: true), - expires_at = table.Column(type: "timestamp with time zone", nullable: true), - tenant_id = table.Column(type: "uuid", nullable: false), - created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") - }, - constraints: table => - { - table.PrimaryKey("pk_tenant_secrets", x => x.id); - table.UniqueConstraint("ak_tenant_secrets_tenant_id_id", x => new { x.tenant_id, x.id }); - table.ForeignKey( - name: "fk_tenant_secrets_tenants_tenant_id", - column: x => x.tenant_id, - principalTable: "tenants", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "ix_tenant_secrets_tenant_id_purpose_provider_secret_key", - table: "tenant_secrets", - columns: new[] { "tenant_id", "purpose", "provider", "secret_key" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "ix_tenant_secrets_tenant_id_purpose_provider_status", - table: "tenant_secrets", - columns: new[] { "tenant_id", "purpose", "provider", "status" }); - - migrationBuilder.CreateIndex( - name: "ix_tenant_secrets_tenant_id_secret_ref", - table: "tenant_secrets", - columns: new[] { "tenant_id", "secret_ref" }, - unique: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "tenant_secrets"); - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726115242_AddPointsPersistenceFoundation.Designer.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726115242_AddPointsPersistenceFoundation.Designer.cs deleted file mode 100644 index af46d3d..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726115242_AddPointsPersistenceFoundation.Designer.cs +++ /dev/null @@ -1,16165 +0,0 @@ -// -using System; -using System.Text.Json; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using Tiku.Infrastructure.Persistence; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - [DbContext(typeof(TikuDbContext))] - [Migration("20260726115242_AddPointsPersistenceFoundation")] - partial class AddPointsPersistenceFoundation - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext"); - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "ltree"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("category_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("SvipQuestionLimit") - .HasColumnType("integer") - .HasColumnName("svip_question_limit"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_categories"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_categories_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_categories_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_categories_tenant_id_node_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_categories_tenant_id_subject_id"); - - b.ToTable("categories", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("StudyTips") - .HasColumnType("text") - .HasColumnName("study_tips"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_majors"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_majors_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_majors_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_majors_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_majors_tenant_id_school_id"); - - b.ToTable("majors", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyModuleId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_module_id"); - - b.Property("LegacyParentId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_parent_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_module_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_module_nodes_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_module_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_module_nodes_tenant_id_module_id"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_module_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_module_nodes_tenant_id_region_id"); - - b.ToTable("module_nodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FullName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("full_name"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsHot") - .HasColumnType("boolean") - .HasColumnName("is_hot"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Pinyin") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("pinyin"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_regions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_regions_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_regions_tenant_id_legacy_id"); - - b.ToTable("regions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsPrimarySchoolModule") - .HasColumnType("boolean") - .HasColumnName("is_primary_school_module"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TextColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("text_color"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_region_modules"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_region_modules_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_region_modules_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_region_modules_tenant_id_region_id"); - - b.ToTable("region_modules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ProfessionalExamDate") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("professional_exam_date"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_schools"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_schools_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_schools_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_schools_tenant_id_module_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_schools_tenant_id_region_id"); - - b.ToTable("schools", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineField", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FieldKey") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("field_key"); - - b.Property("FieldName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("field_name"); - - b.Property("FieldType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("field_type"); - - b.Property("IsFilter") - .HasColumnType("boolean") - .HasColumnName("is_filter"); - - b.Property("IsRequired") - .HasColumnType("boolean") - .HasColumnName("is_required"); - - b.Property("IsTrend") - .HasColumnType("boolean") - .HasColumnName("is_trend"); - - b.Property("IsVisible") - .HasColumnType("boolean") - .HasColumnName("is_visible"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Placeholder") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("placeholder"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Unit") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("unit"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_scoreline_fields"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_scoreline_fields_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_scoreline_fields_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "FieldKey") - .IsUnique() - .HasDatabaseName("ix_scoreline_fields_tenant_id_region_id_field_key"); - - b.HasIndex("TenantId", "RegionId", "IsFilter", "SortOrder") - .HasDatabaseName("ix_scoreline_fields_tenant_id_region_id_is_filter_sort_order"); - - b.ToTable("scoreline_fields", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldValues") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_values") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("major_name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SchoolName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("school_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Year") - .HasColumnType("integer") - .HasColumnName("year"); - - b.HasKey("Id") - .HasName("pk_scoreline_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_scoreline_records_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_scoreline_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_scoreline_records_tenant_id_major_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_scoreline_records_tenant_id_school_id"); - - b.HasIndex("TenantId", "Year") - .HasDatabaseName("ix_scoreline_records_tenant_id_year"); - - b.HasIndex("TenantId", "RegionId", "SchoolId", "MajorId", "Year") - .HasDatabaseName("ix_scoreline_records_tenant_id_region_id_school_id_major_id_ye~"); - - b.ToTable("scoreline_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_subjects_tenant_id_module_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_subjects_tenant_id_node_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_subjects_tenant_id_school_id"); - - b.ToTable("subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AgentUserId") - .HasColumnType("uuid") - .HasColumnName("agent_user_id"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponRedemptionId") - .HasColumnType("uuid") - .HasColumnName("coupon_redemption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("IsUsed") - .HasColumnType("boolean") - .HasColumnName("is_used"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("SoldTo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("sold_to"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitPriceCents") - .HasColumnType("integer") - .HasColumnName("unit_price_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UsedBy") - .HasColumnType("uuid") - .HasColumnName("used_by"); - - b.Property("UsedRegionId") - .HasColumnType("uuid") - .HasColumnName("used_region_id"); - - b.HasKey("Id") - .HasName("pk_activation_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_activation_codes_tenant_id_id"); - - b.HasIndex("AgentUserId") - .HasDatabaseName("ix_activation_codes_agent_user_id"); - - b.HasIndex("UsedBy") - .HasDatabaseName("ix_activation_codes_used_by"); - - b.HasIndex("TenantId", "BatchId") - .HasDatabaseName("ix_activation_codes_tenant_id_batch_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_code"); - - b.HasIndex("TenantId", "CouponRedemptionId") - .HasDatabaseName("ix_activation_codes_tenant_id_coupon_redemption_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UsedRegionId") - .HasDatabaseName("ix_activation_codes_tenant_id_used_region_id"); - - b.ToTable("activation_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CampaignName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("campaign_name"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CostPriceCents") - .HasColumnType("integer") - .HasColumnName("cost_price_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("DefaultUnitPriceCents") - .HasColumnType("integer") - .HasColumnName("default_unit_price_cents"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_code_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_code_batches_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_code_batches_tenant_id_legacy_id"); - - b.ToTable("code_batches", null, t => - { - t.HasCheckConstraint("ck_code_batches_amounts", "default_unit_price_cents >= 0 and cost_price_cents >= 0"); - - t.HasCheckConstraint("ck_code_batches_counts", "total_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillDate") - .HasColumnType("date") - .HasColumnName("bill_date"); - - b.Property("BillType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bill_type"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DuplicateCount") - .HasColumnType("integer") - .HasColumnName("duplicate_count"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IgnoredCount") - .HasColumnType("integer") - .HasColumnName("ignored_count"); - - b.Property("MatchedCount") - .HasColumnType("integer") - .HasColumnName("matched_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MismatchCount") - .HasColumnType("integer") - .HasColumnName("mismatch_count"); - - b.Property("MissingLocalCount") - .HasColumnType("integer") - .HasColumnName("missing_local_count"); - - b.Property("MissingProviderCount") - .HasColumnType("integer") - .HasColumnName("missing_provider_count"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source"); - - b.Property("SourceHash") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("source_name"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_batches_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_batches_created_by"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "Provider", "BillDate", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_provider_bill_dat~"); - - b.ToTable("commerce_reconciliation_batches", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_batches_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_batches_counts", "total_count >= 0 and matched_count >= 0 and mismatch_count >= 0 and missing_local_count >= 0 and missing_provider_count >= 0 and duplicate_count >= 0 and ignored_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("AssignedTo") - .HasColumnType("uuid") - .HasColumnName("assigned_to"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("IssueNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_no"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolutionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("resolution_type"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("summary"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issues_tenant_id_id"); - - b.HasIndex("AssignedTo") - .HasDatabaseName("ix_commerce_reconciliation_issues_assigned_to"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_created_by"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_resolved_by"); - - b.HasIndex("TenantId", "IssueNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_issue_no"); - - b.HasIndex("TenantId", "ItemId") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_item_id") - .HasFilter("item_id is not null and status in ('open', 'investigating', 'escalated')"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "PaymentId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_payment_id_created~"); - - b.HasIndex("TenantId", "AssignedTo", "Status", "DueAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_assigned_to_status~"); - - b.HasIndex("TenantId", "BatchId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_batch_id_status_cr~"); - - b.HasIndex("TenantId", "Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_status_severity_cr~"); - - b.ToTable("commerce_reconciliation_issues", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_issues_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("IssueId") - .HasColumnType("uuid") - .HasColumnName("issue_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issue_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issue_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_actor_user_id"); - - b.HasIndex("TenantId", "IssueId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_tenant_id_issue_id_cre~"); - - b.ToTable("commerce_reconciliation_issue_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LocalStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("local_status"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider_status"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("RefundedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("refunded_at"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_items_tenant_id_id"); - - b.HasIndex("BatchId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_items_batch_id_row_no"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "BatchId", "MatchStatus", "RowNo") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_batch_id_match_stat~"); - - b.HasIndex("TenantId", "Provider", "ProviderTradeNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_provider_provider_t~"); - - b.HasIndex("TenantId", "RefundNo", "ProviderRefundNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_no_provider_~"); - - b.ToTable("commerce_reconciliation_items", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_items_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_items_row", "row_no > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_refund_events_actor_user_id"); - - b.HasIndex("TenantId", "RefundRequestId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_events_tenant_id_refund_request_id_created_~"); - - b.ToTable("commerce_refund_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CancelledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("cancelled_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementAction") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entitlement_action"); - - b.Property("FailedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("failed_at"); - - b.Property("FailureReason") - .HasColumnType("text") - .HasColumnName("failure_reason"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("ProcessedBy") - .HasColumnType("uuid") - .HasColumnName("processed_by"); - - b.Property("Provider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("Reason") - .HasColumnType("text") - .HasColumnName("reason"); - - b.Property("RefundNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RequestedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("requested_at") - .HasDefaultValueSql("now()"); - - b.Property("RequestedBy") - .HasColumnType("uuid") - .HasColumnName("requested_by"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SucceededAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("succeeded_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_requests"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_requests_tenant_id_id"); - - b.HasIndex("ProcessedBy") - .HasDatabaseName("ix_commerce_refund_requests_processed_by"); - - b.HasIndex("RequestedBy") - .HasDatabaseName("ix_commerce_refund_requests_requested_by"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commerce_refund_requests_reviewed_by"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundNo") - .IsUnique() - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_refund_no"); - - b.HasIndex("TenantId", "OrderId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_order_id_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_status_created_at"); - - b.ToTable("commerce_refund_requests", null, t => - { - t.HasCheckConstraint("ck_commerce_refund_requests_amount", "amount_cents > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("discount_type"); - - b.Property("DiscountValue") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("discount_value"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MaxUses") - .HasColumnType("integer") - .HasColumnName("max_uses"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("ValidFrom") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_from"); - - b.Property("ValidTo") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_to"); - - b.HasKey("Id") - .HasName("pk_coupons"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupons_tenant_id_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_code"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupons_tenant_id_plan_id"); - - b.ToTable("coupons", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClaimedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("claimed_at"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponId") - .HasColumnType("uuid") - .HasColumnName("coupon_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountAppliedCents") - .HasColumnType("integer") - .HasColumnName("discount_applied_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_coupon_redemptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupon_redemptions_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_coupon_redemptions_user_id"); - - b.HasIndex("TenantId", "CouponId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_coupon_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_region_id"); - - b.HasIndex("TenantId", "OrderId", "UserId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_order_id_user_id") - .HasFilter("order_id is not null"); - - b.HasIndex("TenantId", "UserId", "CouponId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_user_id_coupon_id") - .HasFilter("coupon_id is not null"); - - b.ToTable("coupon_redemptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("entitlement_type"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("LegacySourceId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_source_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("RevokedBy") - .HasColumnType("uuid") - .HasColumnName("revoked_by"); - - b.Property("RevokedReason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("revoked_reason"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("StartsAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at") - .HasDefaultValueSql("now()"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_entitlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_entitlements_tenant_id_id"); - - b.HasIndex("RevokedBy") - .HasDatabaseName("ix_entitlements_revoked_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_entitlements_user_id"); - - b.HasIndex("TenantId", "UserId", "Status", "ExpiresAt") - .HasDatabaseName("ix_entitlements_tenant_id_user_id_status_expires_at"); - - b.ToTable("entitlements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPlanId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_plan_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("OrderNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PayMethod") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_method"); - - b.Property("PayProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_provider"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("ProductName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("product_name"); - - b.Property("ProductType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("product_type"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("trade_no"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_orders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_orders_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_orders_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderNo") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_order_no"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_orders_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_orders_tenant_id_region_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_orders_tenant_id_user_id_created_at"); - - b.ToTable("orders", null, t => - { - t.HasCheckConstraint("ck_orders_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_orders_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("Quantity") - .HasColumnType("integer") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalAmountCents") - .HasColumnType("integer") - .HasColumnName("total_amount_cents"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_order_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_order_items_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_order_items_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_order_items_tenant_id_order_id"); - - b.ToTable("order_items", null, t => - { - t.HasCheckConstraint("ck_order_items_amounts", "unit_amount_cents >= 0 and total_amount_cents >= 0"); - - t.HasCheckConstraint("ck_order_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyOrderId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_order_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payments_tenant_id_id"); - - b.HasIndex("Provider", "ProviderTradeNo") - .IsUnique() - .HasDatabaseName("ix_payments_provider_provider_trade_no") - .HasFilter("provider_trade_no is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_payments_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId", "UpdatedAt") - .HasDatabaseName("ix_payments_tenant_id_order_id_updated_at"); - - b.ToTable("payments", null, t => - { - t.HasCheckConstraint("ck_payments_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_payments_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("EventId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("event_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("SignatureValid") - .HasColumnType("boolean") - .HasColumnName("signature_valid"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_payment_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payment_events_tenant_id_id"); - - b.HasIndex("Provider", "EventId") - .IsUnique() - .HasDatabaseName("ix_payment_events_provider_event_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_payment_events_tenant_id_payment_id"); - - b.ToTable("payment_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClaimedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("claimed_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Points") - .HasColumnType("integer") - .HasColumnName("points"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TaskId") - .HasColumnType("uuid") - .HasColumnName("task_id"); - - b.Property("TaskKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("task_key"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_point_activity_claims"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_point_activity_claims_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_point_activity_claims_user_id"); - - b.HasIndex("TenantId", "TaskId") - .HasDatabaseName("ix_point_activity_claims_tenant_id_task_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_point_activity_claims_tenant_id_user_id_created_at"); - - b.HasIndex("TenantId", "UserId", "TaskId", "SourceType", "SourceId") - .IsUnique() - .HasDatabaseName("ix_point_activity_claims_tenant_id_user_id_task_id_source_type~") - .HasFilter("source_type is not null and source_id is not null"); - - b.ToTable("point_activity_claims", null, t => - { - t.HasCheckConstraint("ck_point_activity_claims_points", "points > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityTask", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("EndsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("ends_at"); - - b.Property("MaxClaimsPerUser") - .HasColumnType("integer") - .HasColumnName("max_claims_per_user"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Points") - .HasColumnType("integer") - .HasColumnName("points"); - - b.Property("Rules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TaskKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("task_key"); - - b.Property("TaskType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("task_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_point_activity_tasks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_point_activity_tasks_tenant_id_id"); - - b.HasIndex("TenantId", "TaskKey") - .IsUnique() - .HasDatabaseName("ix_point_activity_tasks_tenant_id_task_key"); - - b.HasIndex("TenantId", "Status", "SortOrder") - .HasDatabaseName("ix_point_activity_tasks_tenant_id_status_sort_order"); - - b.ToTable("point_activity_tasks", null, t => - { - t.HasCheckConstraint("ck_point_activity_tasks_max_claims", "max_claims_per_user > 0"); - - t.HasCheckConstraint("ck_point_activity_tasks_points", "points > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("EndsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("ends_at"); - - b.Property("FulfillmentPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("fulfillment_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ItemKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("item_key"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("item_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PointsCost") - .HasColumnType("integer") - .HasColumnName("points_cost"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Stock") - .HasColumnType("integer") - .HasColumnName("stock"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_point_exchange_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_point_exchange_items_tenant_id_id"); - - b.HasIndex("TenantId", "ItemKey") - .IsUnique() - .HasDatabaseName("ix_point_exchange_items_tenant_id_item_key"); - - b.HasIndex("TenantId", "RegionId", "Status", "SortOrder") - .HasDatabaseName("ix_point_exchange_items_tenant_id_region_id_status_sort_order"); - - b.ToTable("point_exchange_items", null, t => - { - t.HasCheckConstraint("ck_point_exchange_items_days", "days is null or days >= 0"); - - t.HasCheckConstraint("ck_point_exchange_items_points_cost", "points_cost > 0"); - - t.HasCheckConstraint("ck_point_exchange_items_stock", "stock is null or stock >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeOrder", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CancelledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("cancelled_at"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FulfillmentSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("fulfillment_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("ItemName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("item_name"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("item_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("OrderedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("ordered_at") - .HasDefaultValueSql("now()"); - - b.Property("PointsCost") - .HasColumnType("integer") - .HasColumnName("points_cost"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_point_exchange_orders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_point_exchange_orders_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_point_exchange_orders_user_id"); - - b.HasIndex("TenantId", "ItemId") - .HasDatabaseName("ix_point_exchange_orders_tenant_id_item_id"); - - b.HasIndex("TenantId", "OrderNo") - .IsUnique() - .HasDatabaseName("ix_point_exchange_orders_tenant_id_order_no"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_point_exchange_orders_tenant_id_user_id_created_at"); - - b.ToTable("point_exchange_orders", null, t => - { - t.HasCheckConstraint("ck_point_exchange_orders_points_cost", "points_cost > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Cover") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cover"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DetailImages") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("detail_images") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true) - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPriceText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_price_text"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("PreviewIframe") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_iframe"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_products"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_products_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_products_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "Type", "IsActive", "SortOrder") - .HasDatabaseName("ix_products_tenant_id_region_id_type_is_active_sort_order"); - - b.ToTable("products", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Badge") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("badge"); - - b.Property("CouponOnly") - .HasColumnType("boolean") - .HasColumnName("coupon_only"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OriginalPriceCents") - .HasColumnType("integer") - .HasColumnName("original_price_cents"); - - b.Property("PerDayLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("per_day_label"); - - b.Property("PriceCents") - .HasColumnType("integer") - .HasColumnName("price_cents"); - - b.Property("Recommended") - .HasColumnType("boolean") - .HasColumnName("recommended"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VpEnabled") - .HasColumnType("boolean") - .HasColumnName("vp_enabled"); - - b.Property("VpProductId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("vp_product_id"); - - b.HasKey("Id") - .HasName("pk_svip_plans"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_svip_plans_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_svip_plans_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_svip_plans_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("svip_plans", null, t => - { - t.HasCheckConstraint("ck_svip_plans_days", "days >= 0"); - - t.HasCheckConstraint("ck_svip_plans_price", "price_cents >= 0 and (original_price_cents is null or original_price_cents >= 0)"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_payment_accounts"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_payment_accounts_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_payment_accounts_tenant_id_provider"); - - b.ToTable("tenant_payment_accounts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillingCycle") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("billing_cycle"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PlanCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("plan_code"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_subscriptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_subscriptions_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "ExpiresAt") - .HasDatabaseName("ix_tenant_subscriptions_tenant_id_status_expires_at"); - - b.ToTable("tenant_subscriptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MetricKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("metric_key"); - - b.Property("MetricValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("metric_value"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_tenant_usage_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_usage_records_tenant_id_id"); - - b.HasIndex("TenantId", "MetricKey", "PeriodStart", "PeriodEnd") - .HasDatabaseName("ix_tenant_usage_records_tenant_id_metric_key_period_start_peri~"); - - b.ToTable("tenant_usage_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContextPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("context_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("GeneratedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("generated_at"); - - b.Property("InputPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("input_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Model") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("model"); - - b.Property("PromptVersion") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("prompt_version"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("ResultPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("result_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_ai_recommendation_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_ai_recommendation_reports_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_ai_recommendation_reports_user_id"); - - b.HasIndex("TenantId", "RegionId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_region_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_user_id_created_at"); - - b.ToTable("ai_recommendation_reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetKey") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_app_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_app_assets_tenant_id_id"); - - b.HasIndex("TenantId", "AssetKey") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_asset_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_legacy_id"); - - b.ToTable("app_assets", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssetKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("AssetType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("asset_type"); - - b.Property("Bucket") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bucket"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("ChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("checksum_sha256"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DownloadCount") - .HasColumnType("integer") - .HasColumnName("download_count"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("FileSizeBytes") - .HasColumnType("bigint") - .HasColumnName("file_size_bytes"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MimeType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mime_type"); - - b.Property("ObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("object_key"); - - b.Property("PreviewObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("preview_object_key"); - - b.Property("PreviewStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("preview_status"); - - b.Property("PreviewUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_url"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SecurityFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScanProvider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("security_scan_provider"); - - b.Property("SecurityScanStatus") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("not_required") - .HasColumnName("security_scan_status"); - - b.Property("SecurityScanSummary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_scan_summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScannedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("security_scanned_at"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StorageProvider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("storage_provider"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UploadStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("upload_status"); - - b.Property("VerificationDetails") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("verification_details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.Property("VerifiedBy") - .HasColumnType("uuid") - .HasColumnName("verified_by"); - - b.Property("VerifiedChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("verified_checksum_sha256"); - - b.Property("VerifiedSizeBytes") - .HasColumnType("bigint") - .HasColumnName("verified_size_bytes"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_assets_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_assets_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_assets_updated_by"); - - b.HasIndex("VerifiedBy") - .HasDatabaseName("ix_content_assets_verified_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_content_assets_tenant_id_category_id"); - - b.HasIndex("TenantId", "ChecksumSha256") - .HasDatabaseName("ix_content_assets_tenant_id_checksum_sha256") - .HasFilter("checksum_sha256 is not null"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_content_assets_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_assets_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PreviewStatus") - .HasDatabaseName("ix_content_assets_tenant_id_preview_status") - .HasFilter("preview_status <> 'none'"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_assets_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_content_assets_tenant_id_subject_id"); - - b.HasIndex("TenantId", "StorageProvider", "Bucket", "ObjectKey") - .HasDatabaseName("ix_content_assets_tenant_id_storage_provider_bucket_object_key"); - - b.HasIndex("TenantId", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_upload_status_status_updated_at"); - - b.HasIndex("TenantId", "SecurityScanStatus", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_security_scan_status_upload_status~"); - - b.HasIndex("TenantId", "Status", "Visibility", "AssetType", "RegionId", "SubjectId", "CategoryId", "SortOrder") - .HasDatabaseName("ix_content_assets_tenant_id_status_visibility_asset_type_regio~"); - - b.ToTable("content_assets", null, t => - { - t.HasCheckConstraint("ck_content_assets_download_count", "download_count >= 0"); - - t.HasCheckConstraint("ck_content_assets_file_size", "file_size_bytes is null or file_size_bytes >= 0"); - - t.HasCheckConstraint("ck_content_assets_verified_checksum", "verified_checksum_sha256 is null or verified_checksum_sha256 ~ '^[a-f0-9]{64}$'"); - - t.HasCheckConstraint("ck_content_assets_verified_size", "verified_size_bytes is null or verified_size_bytes >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_type"); - - b.Property("ActorRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("actor_role"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("AssetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("asset_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DenyCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("deny_code"); - - b.Property("Disposition") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("disposition"); - - b.Property("ExpiresInSeconds") - .HasColumnType("integer") - .HasColumnName("expires_in_seconds"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("SignatureMode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("signature_mode"); - - b.Property("StorageProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("storage_provider"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("Visibility") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_asset_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_content_asset_access_events_user_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_asset_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_user_id_created_at") - .HasFilter("user_id is not null"); - - b.HasIndex("TenantId", "AccessType", "Result", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_access_type_result_cr~"); - - b.ToTable("content_asset_access_events", null, t => - { - t.HasCheckConstraint("ck_content_asset_access_events_expiry", "expires_in_seconds is null or expires_in_seconds > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.PrimitiveCollection("IssueCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("issue_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RiskLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("risk_level"); - - b.Property("ScanStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scan_status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_content_asset_security_scan_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_security_scan_events_tenant_id_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_asset_id_creat~"); - - b.HasIndex("TenantId", "ScanStatus", "RiskLevel", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_scan_status_ri~"); - - b.ToTable("content_asset_security_scan_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("description"); - - b.Property("EntryKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("entry_key"); - - b.Property("EntryType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entry_type"); - - b.Property("Icon") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LayoutConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("layout_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_entries_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_entries_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_entries_updated_by"); - - b.HasIndex("TenantId", "EntryKey") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_entry_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "EntryType", "IsActive", "SortOrder") - .HasDatabaseName("ix_content_entries_tenant_id_region_id_entry_type_is_active_so~"); - - b.ToTable("content_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("Message") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("message"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "ItemId") - .HasDatabaseName("ix_content_import_issues_tenant_id_item_id"); - - b.HasIndex("TenantId", "JobId", "Severity", "RowNo") - .HasDatabaseName("ix_content_import_issues_tenant_id_job_id_severity_row_no"); - - b.ToTable("content_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("content_hash"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExternalId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("external_id"); - - b.Property("IssuesCount") - .HasColumnType("integer") - .HasColumnName("issues_count"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("SourcePayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_items_tenant_id_id"); - - b.HasIndex("JobId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_content_import_items_job_id_row_no"); - - b.HasIndex("TenantId", "JobId", "Status", "RowNo") - .HasDatabaseName("ix_content_import_items_tenant_id_job_id_status_row_no"); - - b.ToTable("content_import_items", null, t => - { - t.HasCheckConstraint("ck_content_import_items_issues_count", "issues_count >= 0"); - - t.HasCheckConstraint("ck_content_import_items_row_no", "row_no >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DryRun") - .HasColumnType("boolean") - .HasColumnName("dry_run"); - - b.Property("ErrorCount") - .HasColumnType("integer") - .HasColumnName("error_count"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("ImportType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("import_type"); - - b.Property("InsertedCount") - .HasColumnType("integer") - .HasColumnName("inserted_count"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SkippedCount") - .HasColumnType("integer") - .HasColumnName("skipped_count"); - - b.Property("SourceFormat") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_format"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TargetCategoryId") - .HasColumnType("uuid") - .HasColumnName("target_category_id"); - - b.Property("TargetContentNodeId") - .HasColumnType("uuid") - .HasColumnName("target_content_node_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TargetRegionId") - .HasColumnType("uuid") - .HasColumnName("target_region_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedCount") - .HasColumnType("integer") - .HasColumnName("updated_count"); - - b.Property("ValidCount") - .HasColumnType("integer") - .HasColumnName("valid_count"); - - b.Property("WarningCount") - .HasColumnType("integer") - .HasColumnName("warning_count"); - - b.HasKey("Id") - .HasName("pk_content_import_jobs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_jobs_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_import_jobs_created_by"); - - b.HasIndex("TenantId", "TargetCategoryId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_category_id"); - - b.HasIndex("TenantId", "TargetContentNodeId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_content_node_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_question_bank_id"); - - b.HasIndex("TenantId", "TargetRegionId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_region_id"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_subject_id"); - - b.HasIndex("TenantId", "ImportType", "Status", "CreatedAt") - .HasDatabaseName("ix_content_import_jobs_tenant_id_import_type_status_created_at"); - - b.ToTable("content_import_jobs", null, t => - { - t.HasCheckConstraint("ck_content_import_jobs_counts", "total_count >= 0 and valid_count >= 0 and error_count >= 0 and warning_count >= 0 and inserted_count >= 0 and updated_count >= 0 and skipped_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Depth") - .HasColumnType("integer") - .HasColumnName("depth"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsLeaf") - .HasColumnType("boolean") - .HasColumnName("is_leaf"); - - b.Property("IsSelectable") - .HasColumnType("boolean") - .HasColumnName("is_selectable"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MarkerConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("marker_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MarkerType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("marker_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("node_key"); - - b.Property("NodeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("node_type"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasColumnType("ltree") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_content_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_nodes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_nodes_created_by"); - - b.HasIndex("Path") - .HasDatabaseName("ix_content_nodes_path"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Path"), "gist"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_nodes_updated_by"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MarkerType") - .HasDatabaseName("ix_content_nodes_tenant_id_marker_type") - .HasFilter("marker_type is not null"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_content_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_nodes_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "NodeKey") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_node_key"); - - b.HasIndex("TenantId", "EntryId", "ParentId", "SortOrder") - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_parent_id_sort_order"); - - b.ToTable("content_nodes", null, t => - { - t.HasCheckConstraint("ck_content_nodes_depth", "depth >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_chapters"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_chapters_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_chapters_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_subject_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_chapters_tenant_id_entry_id_content_node_id_subjec~"); - - b.ToTable("handbook_chapters", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChapterId") - .HasColumnType("uuid") - .HasColumnName("chapter_id"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_entries_tenant_id_id"); - - b.HasIndex("TenantId", "ChapterId") - .HasDatabaseName("ix_handbook_entries_tenant_id_chapter_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_entries_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "ChapterId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_entries_tenant_id_entry_id_content_node_id_chapter~"); - - b.ToTable("handbook_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_school_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_subjects_tenant_id_entry_id_content_node_id_region~"); - - b.ToTable("handbook_subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_images"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_images_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_images_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsPublic") - .HasDatabaseName("ix_images_tenant_id_category_is_public"); - - b.ToTable("images", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssemblyType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assembly_type"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PassScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("pass_score"); - - b.Property("QuestionLimit") - .HasColumnType("integer") - .HasColumnName("question_limit"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Rules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Sections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sections") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_practice_blueprints"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_blueprints_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_practice_blueprints_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_practice_blueprints_updated_by"); - - b.HasIndex("TenantId", "CollectionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_collection_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_practice_blueprints_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_region_id"); - - b.HasIndex("TenantId", "NodeId", "CollectionId", "Mode", "Status", "SortOrder") - .HasDatabaseName("ix_practice_blueprints_tenant_id_node_id_collection_id_mode_st~"); - - b.ToTable("practice_blueprints", null, t => - { - t.HasCheckConstraint("ck_practice_blueprints_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_practice_blueprints_question_limit", "question_limit is null or question_limit > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("AllowedPlanCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("allowed_plan_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.PrimitiveCollection("AllowedRegionIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_region_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedSubjectIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_subject_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedTenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("GrantScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("grant_scope"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_bank_grants"); - - b.HasIndex("AllowedPlanCodes") - .HasDatabaseName("ix_question_bank_grants_allowed_plan_codes"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedPlanCodes"), "gin"); - - b.HasIndex("AllowedTenantIds") - .HasDatabaseName("ix_question_bank_grants_allowed_tenant_ids"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedTenantIds"), "gin"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_bank_grants_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_bank_grants_updated_by"); - - b.HasIndex("SourceQuestionBankId", "Status", "StartsAt", "ExpiresAt") - .HasDatabaseName("ix_question_bank_grants_source_question_bank_id_status_starts_~"); - - b.ToTable("question_bank_grants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CollectionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("collection_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Filters") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("filters") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_collections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collections_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_collections_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_collections_updated_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_question_collections_tenant_id_category_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_question_collections_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_collections_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_question_collections_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_collections_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_question_collections_tenant_id_subject_id"); - - b.HasIndex("TenantId", "NodeId", "Status", "SortOrder") - .HasDatabaseName("ix_question_collections_tenant_id_node_id_status_sort_order"); - - b.ToTable("question_collections", null, t => - { - t.HasCheckConstraint("ck_question_collections_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_question_collections_question_count", "question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Required") - .HasColumnType("boolean") - .HasColumnName("required"); - - b.Property("Score") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_collection_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collection_items_tenant_id_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_question_collection_items_tenant_id_question_id"); - - b.HasIndex("TenantId", "CollectionId", "QuestionId") - .IsUnique() - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_question_~"); - - b.HasIndex("TenantId", "CollectionId", "SectionKey", "SortOrder") - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_section_k~"); - - b.ToTable("question_collection_items", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Types") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("types") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_type_groups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_type_groups_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_type_groups_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_question_type_groups_tenant_id_subject_id_is_active_sort_or~"); - - b.ToTable("question_type_groups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("LegacyVideoId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_video_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoId") - .HasColumnType("uuid") - .HasColumnName("video_id"); - - b.Property("VideoType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("video_type"); - - b.HasKey("Id") - .HasName("pk_question_videos"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_videos_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "VideoId") - .HasDatabaseName("ix_question_videos_tenant_id_video_id"); - - b.HasIndex("TenantId", "QuestionId", "VideoId", "VideoType") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_question_id_video_id_video_type"); - - b.ToTable("question_videos", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("SourceSubjectId") - .HasColumnType("uuid") - .HasColumnName("source_subject_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacySourceSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_source_subject_id"); - - b.Property("LegacyTargetSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_target_subject_id"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.HasKey("TenantId", "SourceSubjectId", "TargetSubjectId") - .HasName("pk_subject_shares"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_subject_shares_tenant_id_target_subject_id"); - - b.ToTable("subject_shares", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AdoptionMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("adoption_mode"); - - b.Property("CopiedQuestionCount") - .HasColumnType("integer") - .HasColumnName("copied_question_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("GrantId") - .HasColumnType("uuid") - .HasColumnName("grant_id"); - - b.Property("LastSyncedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_synced_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("SourceSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SyncStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("sync_status"); - - b.Property("TargetCollectionId") - .HasColumnType("uuid") - .HasColumnName("target_collection_id"); - - b.Property("TargetEntryId") - .HasColumnType("uuid") - .HasColumnName("target_entry_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_question_bank_adoptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_question_bank_adoptions_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_created_by"); - - b.HasIndex("GrantId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_grant_id"); - - b.HasIndex("SourceQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_source_question_bank_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_updated_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .IsUnique() - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_source_question_ba~"); - - b.HasIndex("TenantId", "TargetCollectionId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_collection_~"); - - b.HasIndex("TenantId", "TargetEntryId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_entry_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_question_ba~"); - - b.HasIndex("TenantId", "Status", "UpdatedAt") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_status_updated_at"); - - b.ToTable("tenant_question_bank_adoptions", null, t => - { - t.HasCheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FavoritedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("favorited_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Note") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.HasKey("Id") - .HasName("pk_user_word_favorites"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_favorites_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_favorites_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_favorites_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_favorites_tenant_id_user_id_word_id"); - - b.ToTable("user_word_favorites", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CorrectStreak") - .HasColumnType("integer") - .HasColumnName("correct_streak"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DueLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("due_level"); - - b.Property("EaseFactor") - .ValueGeneratedOnAdd() - .HasPrecision(4, 2) - .HasColumnType("numeric(4,2)") - .HasDefaultValue(2.50m) - .HasColumnName("ease_factor"); - - b.Property("LastResult") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("last_result"); - - b.Property("LastReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_review_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_review_at"); - - b.Property("ReviewCount") - .HasColumnType("integer") - .HasColumnName("review_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_user_word_progress"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_progress_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_progress_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_progress_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_word_id"); - - b.HasIndex("TenantId", "UserId", "NextReviewAt", "Status", "DueLevel") - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_next_review_at_status_~"); - - b.ToTable("user_word_progress", null, t => - { - t.HasCheckConstraint("ck_user_word_progress_correct_streak", "correct_streak >= 0"); - - t.HasCheckConstraint("ck_user_word_progress_ease_factor", "ease_factor >= 1.30 and ease_factor <= 3.00"); - - t.HasCheckConstraint("ck_user_word_progress_review_count", "review_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsGeneral") - .HasColumnType("boolean") - .HasColumnName("is_general"); - - b.Property("KnowledgeTags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("knowledge_tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ThumbnailUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("thumbnail_url"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("video_url"); - - b.HasKey("Id") - .HasName("pk_video_explanations"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_video_explanations_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_video_explanations_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SourceHash") - .HasDatabaseName("ix_video_explanations_tenant_id_source_hash") - .HasFilter("source_hash is not null"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_video_explanations_tenant_id_subject_id_is_active_sort_order"); - - b.ToTable("video_explanations", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WordCount") - .HasColumnType("integer") - .HasColumnName("word_count"); - - b.HasKey("Id") - .HasName("pk_vocabulary_units"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_units_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_units_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_units_tenant_id_entry_id_content_node_id_region_~"); - - b.ToTable("vocabulary_units", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Example") - .HasColumnType("text") - .HasColumnName("example"); - - b.Property("ExampleTranslation") - .HasColumnType("text") - .HasColumnName("example_translation"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Meaning") - .HasColumnType("text") - .HasColumnName("meaning"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phonetic") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("phonetic"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitId") - .HasColumnType("uuid") - .HasColumnName("unit_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Word") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("word"); - - b.HasKey("Id") - .HasName("pk_vocabulary_words"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_words_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_words_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_words_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UnitId", "IsActive", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_vocabulary_words_tenant_id_unit_id_is_active_sort_order_cre~"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "UnitId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_words_tenant_id_entry_id_content_node_id_unit_id~"); - - b.ToTable("vocabulary_words", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("EffectiveRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("effective_rate"); - - b.Property("GeneratedBy") - .HasColumnType("uuid") - .HasColumnName("generated_by"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidBy") - .HasColumnType("uuid") - .HasColumnName("paid_by"); - - b.Property("PaidUserCount") - .HasColumnType("integer") - .HasColumnName("paid_user_count"); - - b.Property("PaymentAccount") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("payment_account"); - - b.Property("PaymentMethod") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_method"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("SettlementNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("settlement_no"); - - b.Property("SourceCount") - .HasColumnType("integer") - .HasColumnName("source_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlements_tenant_id_id"); - - b.HasIndex("GeneratedBy") - .HasDatabaseName("ix_commission_settlements_generated_by"); - - b.HasIndex("PaidBy") - .HasDatabaseName("ix_commission_settlements_paid_by"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlements_referrer_user_id"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commission_settlements_reviewed_by"); - - b.HasIndex("TenantId", "SettlementNo") - .IsUnique() - .HasDatabaseName("ix_commission_settlements_tenant_id_settlement_no"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart") - .HasDatabaseName("ix_commission_settlements_referrer_start"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart", "PeriodEnd") - .IsUnique() - .HasDatabaseName("ux_commission_settlements_referrer_period"); - - b.ToTable("commission_settlements", null, t => - { - t.HasCheckConstraint("ck_commission_settlements_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_counts", "source_count >= 0 and paid_user_count >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_period", "period_end >= period_start"); - - t.HasCheckConstraint("ck_commission_settlements_rates", "default_rate >= 0 and default_rate <= 1 and (effective_rate is null or (effective_rate >= 0 and effective_rate <= 1))"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AttributionType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("attribution_type"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RateSource") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("rate_source"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("SettlementId") - .HasColumnType("uuid") - .HasColumnName("settlement_id"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_no"); - - b.Property("SourcePaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("source_paid_at"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlement_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlement_items_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlement_items_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_commission_settlement_items_student_user_id"); - - b.HasIndex("TenantId", "SettlementId") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_settlement_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "SourcePaidAt") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_referrer_user_id_sour~"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .IsUnique() - .HasDatabaseName("ix_commission_settlement_items_tenant_id_source_type_source_id"); - - b.ToTable("commission_settlement_items", null, t => - { - t.HasCheckConstraint("ck_commission_settlement_items_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlement_items_rate", "commission_rate >= 0 and commission_rate <= 1"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignmentConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssignmentCursor") - .HasColumnType("integer") - .HasColumnName("assignment_cursor"); - - b.Property("AssignmentMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("AssignmentPool") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_pool") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DelaySeconds") - .HasColumnType("integer") - .HasColumnName("delay_seconds"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("ExamType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("exam_type"); - - b.Property("FormName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("form_name"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Url") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("url"); - - b.HasKey("Id") - .HasName("pk_crm_config"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_config_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_crm_config_tenant_id"); - - b.HasIndex("TenantId", "AssignmentMode") - .HasDatabaseName("ix_crm_config_tenant_id_assignment_mode"); - - b.ToTable("crm_config", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempt") - .HasColumnType("integer") - .HasColumnName("attempt"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("HttpCode") - .HasColumnType("integer") - .HasColumnName("http_code"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Outcome") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("outcome"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("RequestBody") - .HasColumnType("text") - .HasColumnName("request_body"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ResponseSummary") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("response_summary"); - - b.Property("SignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("signed_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_log"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_log_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_log_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_log_tenant_id_record_id_created_at"); - - b.ToTable("crm_webhook_log", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("ScheduledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Source") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("target_url"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_queue"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_queue_tenant_id_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_idempotency_key") - .HasFilter("idempotency_key is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id_created_at") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "Source", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_source_created_at") - .HasFilter("source = 'tenant.student.crm_push'"); - - b.HasIndex("TenantId", "Status", "ScheduledAt", "NextAttemptAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_status_scheduled_at_next_attemp~"); - - b.ToTable("crm_webhook_queue", null, t => - { - t.HasCheckConstraint("ck_crm_webhook_queue_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LandingPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("landing_path"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_codes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_codes_user_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_code"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_user_id"); - - b.HasIndex("TenantId", "Code", "Status") - .HasDatabaseName("ix_referral_codes_tenant_id_code_status"); - - b.ToTable("referral_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("assigned_at"); - - b.Property("AssignedBy") - .HasColumnType("uuid") - .HasColumnName("assigned_by"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("AssignmentMode") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("BindType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bind_type"); - - b.Property("BoundAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("bound_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FirstTrackId") - .HasColumnType("uuid") - .HasColumnName("first_track_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ProtectedUntil") - .HasColumnType("timestamp with time zone") - .HasColumnName("protected_until"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_leads"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_leads_tenant_id_id"); - - b.HasIndex("AssignedBy") - .HasDatabaseName("ix_referral_leads_assigned_by"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_referral_leads_assigned_to_user_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_leads_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_referral_leads_student_user_id"); - - b.HasIndex("TenantId", "FirstTrackId") - .HasDatabaseName("ix_referral_leads_tenant_id_first_track_id"); - - b.HasIndex("TenantId", "StudentUserId") - .IsUnique() - .HasDatabaseName("ix_referral_leads_tenant_id_student_user_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_referrer_user_id_bound_at"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_assigned_to_user_id_status_bound_at"); - - b.ToTable("referral_leads", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Page") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("page"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("QrcodeUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("qrcode_url"); - - b.Property("RefCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("Scene") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("scene"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_qrcodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_qrcodes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_qrcodes_user_id"); - - b.HasIndex("TenantId", "RefCode", "Status") - .HasDatabaseName("ix_referral_qrcodes_tenant_id_ref_code_status"); - - b.HasIndex("TenantId", "Provider", "Scene", "Page") - .IsUnique() - .HasDatabaseName("ix_referral_qrcodes_tenant_id_provider_scene_page"); - - b.ToTable("referral_qrcodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LeaderUserId") - .HasColumnType("uuid") - .HasColumnName("leader_user_id"); - - b.Property("MemberUserId") - .HasColumnType("uuid") - .HasColumnName("member_user_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RelationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("relation_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_team_edges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_team_edges_tenant_id_id"); - - b.HasIndex("LeaderUserId") - .HasDatabaseName("ix_referral_team_edges_leader_user_id"); - - b.HasIndex("MemberUserId") - .HasDatabaseName("ix_referral_team_edges_member_user_id"); - - b.HasIndex("TenantId", "LeaderUserId", "Status") - .HasDatabaseName("ix_referral_team_edges_tenant_id_leader_user_id_status"); - - b.HasIndex("TenantId", "MemberUserId", "RelationType") - .IsUnique() - .HasDatabaseName("ix_referral_team_edges_tenant_id_member_user_id_relation_type"); - - b.ToTable("referral_team_edges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("LeadId") - .HasColumnType("uuid") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("TargetUserId") - .HasColumnType("uuid") - .HasColumnName("target_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_referral_tracks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_tracks_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_tracks_referrer_user_id"); - - b.HasIndex("TargetUserId") - .HasDatabaseName("ix_referral_tracks_target_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_referral_tracks_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "LeadId", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_lead_id_created_at"); - - b.HasIndex("TenantId", "RefCode", "EventType", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_ref_code_event_type_created_at"); - - b.ToTable("referral_tracks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("MinSettlementCents") - .HasColumnType("integer") - .HasColumnName("min_settlement_cents"); - - b.Property("SettlementCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("settlement_cycle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_commission_settings"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_commission_settings_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_commission_settings_tenant_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_commission_settings_updated_by"); - - b.ToTable("tenant_commission_settings", null, t => - { - t.HasCheckConstraint("ck_tenant_commission_settings_default_rate", "default_rate >= 0 and default_rate <= 1"); - - t.HasCheckConstraint("ck_tenant_commission_settings_min_settlement", "min_settlement_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AvatarPreset") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("male") - .HasColumnName("avatar_preset"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastCheckInDate") - .HasColumnType("date") - .HasColumnName("last_check_in_date"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("MasteredWordsCount") - .HasColumnType("integer") - .HasColumnName("mastered_words_count"); - - b.Property("ModuleSelections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_selections") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Progress") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("progress") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionsAnsweredToday") - .HasColumnType("integer") - .HasColumnName("questions_answered_today"); - - b.Property("RecentActivities") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("recent_activities") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SelectedMajorId") - .HasColumnType("uuid") - .HasColumnName("selected_major_id"); - - b.Property("SelectedSchoolId") - .HasColumnType("uuid") - .HasColumnName("selected_school_id"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_student_profiles"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_student_profiles_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_student_profiles_user_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_student_profiles_tenant_id_region_id"); - - b.HasIndex("TenantId", "SelectedMajorId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_major_id"); - - b.HasIndex("TenantId", "SelectedSchoolId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_school_id"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_student_profiles_tenant_id_user_id"); - - b.ToTable("student_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AvatarUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("avatar_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("LastSeenAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPasswordHash") - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasColumnName("legacy_password_hash"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PasswordMigrationRequired") - .HasColumnType("boolean") - .HasColumnName("password_migration_required"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("PrimaryRole") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("primary_role"); - - b.Property("RawProfile") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_profile") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Score") - .HasColumnType("integer") - .HasColumnName("score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Username") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Email") - .IsUnique() - .HasDatabaseName("ix_users_email"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_users_legacy_id"); - - b.HasIndex("Phone") - .IsUnique() - .HasDatabaseName("ix_users_phone"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("OpenId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("open_id"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderSubject") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("provider_subject"); - - b.Property("SecretPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("secret_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UnionId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("union_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_identities"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_identities_user_id"); - - b.HasIndex("Provider", "ProviderSubject") - .IsUnique() - .HasDatabaseName("ix_user_identities_provider_provider_subject"); - - b.ToTable("user_identities", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("IssueCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("RawValueSample") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("raw_value_sample"); - - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_run_id"); - - b.HasIndex("RunId", "Severity", "CollectionName") - .HasDatabaseName("ix_pb_import_issues_run_id_severity_collection_name"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("SourceKind") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_kind"); - - b.Property("SourceName") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_runs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_runs_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "StartedAt") - .HasDatabaseName("ix_pb_import_runs_tenant_id_status_started_at"); - - b.ToTable("pb_import_runs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("CollectionName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Errors") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("errors") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ImportedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("imported_at") - .HasDefaultValueSql("now()"); - - b.Property("Normalized") - .HasColumnType("boolean") - .HasColumnName("normalized"); - - b.Property("Record") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("record") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("RunId", "CollectionName", "LegacyId") - .HasName("pk_pb_raw_records"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_run_id"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_raw_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("AnsweredAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("answered_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsCorrect") - .HasColumnType("boolean") - .HasColumnName("is_correct"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("QuestionVersionId") - .HasColumnType("uuid") - .HasColumnName("question_version_id"); - - b.Property("SelectedOptions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("selected_options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_answer_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_answer_records_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_answer_records_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_answer_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id"); - - b.HasIndex("TenantId", "QuestionId", "QuestionVersionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id_question_version_id"); - - b.HasIndex("TenantId", "UserId", "PracticeSessionId") - .HasDatabaseName("ix_answer_records_tenant_id_user_id_practice_session_id"); - - b.ToTable("answer_records", null, t => - { - t.HasCheckConstraint("ck_answer_records_version_requires_question", "question_version_id is null or question_id is not null"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActiveUsers") - .HasColumnType("integer") - .HasColumnName("active_users"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("NewOrders") - .HasColumnType("integer") - .HasColumnName("new_orders"); - - b.Property("NewQuestions") - .HasColumnType("integer") - .HasColumnName("new_questions"); - - b.Property("NewRevenueCents") - .HasColumnType("integer") - .HasColumnName("new_revenue_cents"); - - b.Property("NewUsers") - .HasColumnType("integer") - .HasColumnName("new_users"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_dashboard_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_dashboard_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_stat_date_region_id"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId"), false); - - b.ToTable("dashboard_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("ExamAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("exam_at"); - - b.Property("ExamName") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("exam_name"); - - b.Property("ExamType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("exam_type"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_exam_dates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_exam_dates_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_exam_dates_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_exam_dates_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId", "ExamAt") - .HasDatabaseName("ix_exam_dates_tenant_id_school_id_exam_at"); - - b.ToTable("exam_dates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_favorite_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_favorite_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_favorite_questions_tenant_id_question_id"); - - b.ToTable("favorite_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_mode"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementId") - .HasColumnType("uuid") - .HasColumnName("entitlement_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("GrantedCount") - .HasColumnType("integer") - .HasColumnName("granted_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("Reason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("reason"); - - b.Property("RequestedCount") - .HasColumnType("integer") - .HasColumnName("requested_count"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_access_events_user_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_access_events_tenant_id_practice_session_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_practice_access_events_tenant_id_user_id_created_at"); - - b.ToTable("practice_access_events", null, t => - { - t.HasCheckConstraint("ck_practice_access_events_consumed_free_quota", "consumed_free_quota >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_granted_count", "granted_count >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_requested_count", "requested_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FreeLimit") - .HasColumnType("integer") - .HasColumnName("free_limit"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsageDate") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasColumnName("usage_date") - .HasDefaultValueSql("current_date"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_daily_usage"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_daily_usage_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_daily_usage_user_id"); - - b.HasIndex("TenantId", "UserId", "UsageDate") - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date"); - - b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId") - .IsUnique() - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date_scope_typ~"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId"), false); - - b.ToTable("practice_daily_usage", null, t => - { - t.HasCheckConstraint("ck_practice_daily_usage_free_limit", "free_limit >= 0"); - - t.HasCheckConstraint("ck_practice_daily_usage_used_count", "used_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessEntitlementId") - .HasColumnType("uuid") - .HasColumnName("access_entitlement_id"); - - b.Property("AccessMode") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("free") - .HasColumnName("access_mode"); - - b.Property("AccessSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_sessions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "UserId", "Id") - .HasName("ak_practice_sessions_tenant_id_user_id_id"); - - 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"); - - b.ToTable("practice_sessions", null, t => - { - t.HasCheckConstraint("ck_practice_sessions_consumed_free_quota", "consumed_free_quota >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionResults") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_results") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionStats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("section_stats") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("SubmittedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("submitted_at") - .HasDefaultValueSql("now()"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalQuestions") - .HasColumnType("integer") - .HasColumnName("total_questions"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.Property("WrongQuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("wrong_question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.HasKey("Id") - .HasName("pk_practice_session_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_reports_tenant_id_id"); - - 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.ToTable("practice_session_reports", null, t => - { - t.HasCheckConstraint("ck_practice_session_reports_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_reports_counts", "total_questions >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_duration", "duration_seconds >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("question_type"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SectionName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("section_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_practice_session_report_sections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_report_sections_tenant_id_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_practice_session~"); - - b.HasIndex("TenantId", "ReportId", "SectionKey") - .IsUnique() - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sectio~"); - - b.HasIndex("TenantId", "ReportId", "SortOrder") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sort_o~"); - - b.ToTable("practice_session_report_sections", null, t => - { - t.HasCheckConstraint("ck_practice_session_report_sections_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_report_sections_counts", "question_count >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_report_sections_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastAccessAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_access_at"); - - b.Property("LastPracticeAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_practice_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("practice_type"); - - b.Property("Progress") - .HasColumnType("integer") - .HasColumnName("progress"); - - b.Property("TargetLegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("target_legacy_id"); - - b.Property("TargetName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("target_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_recent_practices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_recent_practices_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_recent_practices_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_recent_practices_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "LastAccessAt") - .HasDatabaseName("ix_recent_practices_tenant_id_user_id_last_access_at"); - - b.ToTable("recent_practices", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attachments") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("attachments") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("Contact") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("contact"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("HandledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("handled_at"); - - b.Property("HandledBy") - .HasColumnType("uuid") - .HasColumnName("handled_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Resolution") - .HasColumnType("text") - .HasColumnName("resolution"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_reports_tenant_id_id"); - - b.HasIndex("HandledBy") - .HasDatabaseName("ix_reports_handled_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_reports_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_reports_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_question_id_created_at") - .HasFilter("question_id is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_status_created_at"); - - b.ToTable("reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_report_status_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_report_status_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_report_status_events_actor_user_id"); - - b.HasIndex("TenantId", "ReportId", "CreatedAt") - .HasDatabaseName("ix_report_status_events_tenant_id_report_id_created_at"); - - b.ToTable("report_status_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CodeCount") - .HasColumnType("integer") - .HasColumnName("code_count"); - - b.Property("CodeEstimated") - .HasColumnType("integer") - .HasColumnName("code_estimated"); - - b.Property("CodeUsed") - .HasColumnType("integer") - .HasColumnName("code_used"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EstimatedRevenueCents") - .HasColumnType("integer") - .HasColumnName("estimated_revenue_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("OrderCount") - .HasColumnType("integer") - .HasColumnName("order_count"); - - b.Property("RealRevenueCents") - .HasColumnType("integer") - .HasColumnName("real_revenue_cents"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_revenue_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_revenue_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_stat_date_region_id_sale_type"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType"), false); - - b.ToTable("revenue_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceAfter") - .HasColumnType("integer") - .HasColumnName("balance_after"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Points") - .HasColumnType("integer") - .HasColumnName("points"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_score_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_score_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_score_events_user_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_user_score_events_tenant_id_idempotency_key"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_user_score_events_tenant_id_user_id_created_at"); - - b.ToTable("user_score_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("LastWrongAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_wrong_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("WrongCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(1) - .HasColumnName("wrong_count"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_wrong_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_wrong_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_wrong_questions_tenant_id_question_id"); - - b.ToTable("wrong_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_announcements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_announcements_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_announcements_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "IsActive", "SortOrder") - .HasDatabaseName("ix_announcements_tenant_id_is_active_sort_order"); - - b.ToTable("announcements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_audit_logs"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_audit_logs_actor_user_id"); - - b.HasIndex("TenantId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_created_at"); - - b.HasIndex("TenantId", "TargetType", "TargetId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_target_type_target_id_created_at"); - - b.ToTable("audit_logs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("ConditionExtra") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("condition_extra") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ConditionField") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("condition_field"); - - b.Property("ConditionOperator") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("condition_operator"); - - b.Property("ConditionValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("condition_value"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon_url"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Level") - .HasColumnType("integer") - .HasColumnName("level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnlockType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("unlock_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_badges_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsActive", "SortOrder") - .HasDatabaseName("ix_badges_tenant_id_category_is_active_sort_order"); - - b.ToTable("badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("BorderColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("border_color"); - - b.Property("ButtonLink") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("button_link"); - - b.Property("ButtonText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("button_text"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Subtitle") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("subtitle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_banners"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_banners_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_banners_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_banners_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("banners", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Answer") - .HasColumnType("text") - .HasColumnName("answer"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Question") - .HasColumnType("text") - .HasColumnName("question"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_faqs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_faqs_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_faqs_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_faqs_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("faqs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("AdoptionId") - .HasColumnType("uuid") - .HasColumnName("adoption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("ReadBy") - .HasColumnType("uuid") - .HasColumnName("read_by"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_content_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_content_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_content_notifications_created_by"); - - b.HasIndex("ReadBy") - .HasDatabaseName("ix_tenant_content_notifications_read_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_source_question_bank~"); - - b.HasIndex("TenantId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_notification_type_de~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "AdoptionId", "NotificationType", "Status") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_adoption_id_notifica~"); - - b.ToTable("tenant_content_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActivePublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ActiveTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("active_template_code"); - - b.Property("ActiveTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DraftPublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("draft_template_code"); - - b.Property("DraftTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftUpdatedBy") - .HasColumnType("uuid") - .HasColumnName("draft_updated_by"); - - b.Property("PublishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("published_at"); - - b.Property("PublishedBy") - .HasColumnType("uuid") - .HasColumnName("published_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_configs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_theme_configs_tenant_id_id"); - - b.HasIndex("ActiveTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_active_template_code"); - - b.HasIndex("DraftTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_draft_template_code"); - - b.HasIndex("DraftUpdatedBy") - .HasDatabaseName("ix_tenant_theme_configs_draft_updated_by"); - - b.HasIndex("PublishedBy") - .HasDatabaseName("ix_tenant_theme_configs_published_by"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_configs_tenant_id"); - - b.ToTable("tenant_theme_configs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PreviewImageUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_image_url"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_templates"); - - b.HasAlternateKey("Code") - .HasName("ak_tenant_theme_templates_code"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_templates_code"); - - b.HasIndex("Status", "SortOrder", "Code") - .HasDatabaseName("ix_tenant_theme_templates_status_sort_order_code"); - - b.ToTable("tenant_theme_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BadgeId") - .HasColumnType("uuid") - .HasColumnName("badge_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrantedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("granted_at"); - - b.Property("GrantedBy") - .HasColumnType("uuid") - .HasColumnName("granted_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_badges_tenant_id_id"); - - b.HasIndex("GrantedBy") - .HasDatabaseName("ix_user_badges_granted_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_badges_user_id"); - - b.HasIndex("TenantId", "BadgeId") - .HasDatabaseName("ix_user_badges_tenant_id_badge_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "BadgeId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_user_id_badge_id"); - - b.ToTable("user_badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_user_notifications_created_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_notifications_user_id"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .HasDatabaseName("ix_user_notifications_tenant_id_source_type_source_id"); - - b.HasIndex("TenantId", "UserId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_notification_type_dedu~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "UserId", "Status", "CreatedAt") - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_status_created_at"); - - b.ToTable("user_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("AcknowledgedBy") - .HasColumnType("uuid") - .HasColumnName("acknowledged_by"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("AuditLogId") - .HasColumnType("uuid") - .HasColumnName("audit_log_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FirstSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("first_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("LastSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("RuleId") - .HasColumnType("uuid") - .HasColumnName("rule_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alerts"); - - b.HasIndex("AcknowledgedBy") - .HasDatabaseName("ix_platform_audit_alerts_acknowledged_by"); - - b.HasIndex("AuditLogId") - .HasDatabaseName("ix_platform_audit_alerts_audit_log_id"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_platform_audit_alerts_resolved_by"); - - b.HasIndex("RuleId", "AuditLogId") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alerts_rule_id_audit_log_id"); - - b.HasIndex("Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_status_severity_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_tenant_id_status_created_at"); - - b.ToTable("platform_audit_alerts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("ActionPatterns") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("action_patterns") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("Conditions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("conditions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.PrimitiveCollection("TargetTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("target_types") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alert_rules"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alert_rules_code"); - - b.HasIndex("TenantId") - .HasDatabaseName("ix_platform_audit_alert_rules_tenant_id"); - - b.HasIndex("Enabled", "Severity", "Code") - .HasDatabaseName("ix_platform_audit_alert_rules_enabled_severity_code"); - - b.ToTable("platform_audit_alert_rules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationChannel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChannelCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel_code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MinReminderLevel") - .HasColumnType("integer") - .HasColumnName("min_reminder_level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.PrimitiveCollection("ReminderChannels") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_channels") - .HasDefaultValueSql("array['internal']::text[]"); - - b.PrimitiveCollection("ReminderTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_types") - .HasDefaultValueSql("array['overdue', 'final_notice']::text[]"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.PrimitiveCollection("TenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WebhookUrl") - .IsRequired() - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("webhook_url"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_channels"); - - b.HasIndex("ChannelCode") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_channels_channel_code"); - - b.HasIndex("Enabled", "MinReminderLevel", "ChannelCode") - .HasDatabaseName("ix_platform_dunning_notification_channels_enabled_min_reminder~"); - - b.ToTable("platform_dunning_notification_channels", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_channels_level", "min_reminder_level between 1 and 20"); - - t.HasCheckConstraint("ck_platform_dunning_channels_timeout", "timeout_seconds between 1 and 60"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("ChannelId") - .HasColumnType("uuid") - .HasColumnName("channel_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.Property("ReminderId") - .HasColumnType("uuid") - .HasColumnName("reminder_id"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScheduledAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at") - .HasDefaultValueSql("now()"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_platform_dunning_notification_events_tenant_id_id"); - - b.HasIndex("ChannelId", "ReminderId") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_events_channel_id_reminder_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_invoice_id"); - - b.HasIndex("TenantId", "ReminderId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_reminder_id"); - - b.HasIndex("InvoiceId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_invoice_id_status_crea~"); - - b.HasIndex("ReminderId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_reminder_id_status_cre~"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_status_creat~"); - - b.HasIndex("Status", "NextAttemptAt", "ScheduledAt", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_status_next_attempt_at~"); - - b.ToTable("platform_dunning_notification_events", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_events_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformSaasPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseAmountCents") - .HasColumnType("integer") - .HasColumnName("base_amount_cents"); - - b.Property("BillingCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_cycle"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IncludedQuotas") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("included_quotas") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OveragePrices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("overage_prices") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_saas_plans"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_saas_plans_code"); - - b.HasIndex("Status", "SortOrder") - .HasDatabaseName("ix_platform_saas_plans_status_sort_order"); - - b.ToTable("platform_saas_plans", null, t => - { - t.HasCheckConstraint("ck_platform_saas_plans_amount", "base_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BankAccountMasked") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("bank_account_masked"); - - b.Property("BankName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bank_name"); - - b.Property("BillingAddress") - .HasColumnType("text") - .HasColumnName("billing_address"); - - b.Property("BillingName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("billing_name"); - - b.Property("ContactEmail") - .HasMaxLength(300) - .HasColumnType("citext") - .HasColumnName("contact_email"); - - b.Property("ContactName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("contact_name"); - - b.Property("ContactPhone") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("contact_phone"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceTitle") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("invoice_title"); - - b.Property("InvoiceType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TaxId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("tax_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_billing_profiles"); - - b.ToTable("tenant_billing_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceCents") - .HasColumnType("integer") - .HasColumnName("balance_cents"); - - b.Property("BillingPeriodEnd") - .HasColumnType("date") - .HasColumnName("billing_period_end"); - - b.Property("BillingPeriodStart") - .HasColumnType("date") - .HasColumnName("billing_period_start"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("DiscountCents") - .HasColumnType("integer") - .HasColumnName("discount_cents"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("invoice_no"); - - b.Property("InvoiceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidCents") - .HasColumnType("integer") - .HasColumnName("paid_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubtotalCents") - .HasColumnType("integer") - .HasColumnName("subtotal_cents"); - - b.Property("TaxCents") - .HasColumnType("integer") - .HasColumnName("tax_cents"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCents") - .HasColumnType("integer") - .HasColumnName("total_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoices_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoices_created_by"); - - b.HasIndex("InvoiceNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_invoice_no"); - - b.HasIndex("TenantId", "BillingPeriodStart", "BillingPeriodEnd") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_tenant_id_billing_period_start_billing_peri~") - .HasFilter("invoice_type = 'usage_overage' and status <> 'void' and metadata->>'source' = 'usage_overage_auto'"); - - b.HasIndex("TenantId", "Status", "DueDate") - .HasDatabaseName("ix_tenant_invoices_tenant_id_status_due_date"); - - b.ToTable("tenant_invoices", null, t => - { - t.HasCheckConstraint("ck_tenant_invoices_amounts", "subtotal_cents >= 0 and discount_cents >= 0 and tax_cents >= 0 and total_cents >= 0 and paid_cents >= 0 and balance_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoices_period", "billing_period_end is null or billing_period_start is null or billing_period_end >= billing_period_start"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("description"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("ItemRefId") - .HasColumnType("uuid") - .HasColumnName("item_ref_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Quantity") - .HasPrecision(12, 2) - .HasColumnType("numeric(12,2)") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_items_tenant_id_id"); - - b.HasIndex("InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_invoice_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_items", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_items_amounts", "unit_amount_cents >= 0 and amount_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_no"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReceivedBy") - .HasColumnType("uuid") - .HasColumnName("received_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_payments_tenant_id_id"); - - b.HasIndex("PaymentNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_payments_payment_no"); - - b.HasIndex("ReceivedBy") - .HasDatabaseName("ix_tenant_invoice_payments_received_by"); - - b.HasIndex("InvoiceId", "Status") - .HasDatabaseName("ix_tenant_invoice_payments_invoice_id_status"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_payments_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_payments", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("BalanceCentsSnapshot") - .HasColumnType("integer") - .HasColumnName("balance_cents_snapshot"); - - b.Property("Channel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("channel"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Message") - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReminderDate") - .HasColumnType("date") - .HasColumnName("reminder_date"); - - b.Property("ReminderLevel") - .HasColumnType("integer") - .HasColumnName("reminder_level"); - - b.Property("ReminderType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("reminder_type"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_reminders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_reminders_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoice_reminders_created_by"); - - b.HasIndex("InvoiceId", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_invoice_id_reminder_date"); - - b.HasIndex("TenantId", "Status", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_status_reminder_date"); - - b.HasIndex("TenantId", "InvoiceId", "ReminderType", "Channel", "ReminderDate") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_invoice_id_reminder_type~"); - - b.ToTable("tenant_invoice_reminders", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_reminders_balance", "balance_cents_snapshot >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_reminders_level", "reminder_level between 1 and 20"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CurrentVersionId") - .HasColumnType("uuid") - .HasColumnName("current_version_id"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExamMarkers") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("exam_markers") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("HasVideoExplanation") - .HasColumnType("boolean") - .HasColumnName("has_video_explanation"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyNodeId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_node_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("MediaUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("media_url"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PrimaryCollectionId") - .HasColumnType("uuid") - .HasColumnName("primary_collection_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("TypeLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("type_label"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_questions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_questions_tenant_id_id"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_questions_tenant_id_category_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_questions_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_questions_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_questions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_questions_tenant_id_node_id"); - - b.HasIndex("TenantId", "PrimaryCollectionId") - .HasDatabaseName("ix_questions_tenant_id_primary_collection_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_questions_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_questions_tenant_id_subject_id"); - - b.HasIndex("TenantId", "Id", "CurrentVersionId") - .HasDatabaseName("ix_questions_tenant_id_id_current_version_id"); - - b.ToTable("questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SourceScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_scope"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_banks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_banks_tenant_id_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_banks_tenant_id_region_id"); - - b.ToTable("question_banks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("CodeLang") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code_lang"); - - b.Property("CodeTemplate") - .HasColumnType("text") - .HasColumnName("code_template"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CorrectOptionIndex") - .HasColumnType("integer") - .HasColumnName("correct_option_index"); - - b.Property("CorrectOptionIndices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("correct_option_indices") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Explanation") - .HasColumnType("text") - .HasColumnName("explanation"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubQuestions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sub_questions") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("VersionNo") - .HasColumnType("integer") - .HasColumnName("version_no"); - - b.HasKey("Id") - .HasName("pk_question_versions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_versions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "QuestionId", "Id") - .HasName("ak_question_versions_tenant_id_question_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_versions_created_by"); - - b.HasIndex("QuestionId", "VersionNo") - .IsUnique() - .HasDatabaseName("ix_question_versions_question_id_version_no"); - - b.ToTable("question_versions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FailureCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("failure_code"); - - b.Property("Identifier") - .HasMaxLength(320) - .HasColumnType("character varying(320)") - .HasColumnName("identifier"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_login_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_login_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_login_events_user_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_auth_login_events_tenant_id_user_id_created_at"); - - b.ToTable("auth_login_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TokenHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("token_hash"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_sessions_tenant_id_id"); - - b.HasIndex("TokenHash") - .IsUnique() - .HasDatabaseName("ix_auth_sessions_token_hash"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_sessions_user_id"); - - b.HasIndex("TenantId", "UserId", "ExpiresAt") - .HasDatabaseName("ix_auth_sessions_tenant_id_user_id_expires_at") - .HasFilter("revoked_at is null"); - - b.ToTable("auth_sessions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Dimension") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("dimension"); - - b.Property("ScopeHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("scope_hash"); - - b.Property("BucketStart") - .HasColumnType("timestamp with time zone") - .HasColumnName("bucket_start"); - - b.Property("RequestCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(0) - .HasColumnName("request_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId", "Dimension", "ScopeHash", "BucketStart") - .HasName("pk_sms_send_rate_limits"); - - b.HasIndex("UpdatedAt") - .HasDatabaseName("ix_sms_send_rate_limits_updated_at"); - - b.ToTable("sms_send_rate_limits", null, t => - { - t.HasCheckConstraint("ck_sms_send_rate_limits_request_count", "request_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CodeHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("code_hash"); - - b.Property("ConsumedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("consumed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phone") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Purpose") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("purpose"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_sms_verification_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_sms_verification_codes_tenant_id_id"); - - b.HasIndex("TenantId", "Phone", "Purpose") - .IsUnique() - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "CreatedAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_created_at") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "ExpiresAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_expires_at"); - - b.ToTable("sms_verification_codes", null, t => - { - t.HasCheckConstraint("ck_sms_verification_codes_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BillingStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_status"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegalName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("legal_name"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OwnerUserId") - .HasColumnType("uuid") - .HasColumnName("owner_user_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("slug"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenants"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenants_legacy_id"); - - b.HasIndex("OwnerUserId") - .HasDatabaseName("ix_tenants_owner_user_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_tenants_slug"); - - b.ToTable("tenants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_auth_providers"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_auth_providers_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider"); - - b.HasIndex("TenantId", "Provider", "Status") - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider_status"); - - b.ToTable("tenant_auth_providers", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BrandName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("brand_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FaviconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("favicon_url"); - - b.Property("LogoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("logo_url"); - - b.Property("OrganizationName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("organization_name"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ServiceAccountName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("service_account_name"); - - b.Property("ServiceWechat") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("service_wechat"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("Slogan") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("slogan"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_branding"); - - b.ToTable("tenant_branding", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_classes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_classes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_classes_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_classes_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_code") - .HasFilter("code is not null and code <> ''"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_tenant_classes_tenant_id_region_id") - .HasFilter("region_id is not null"); - - b.HasIndex("TenantId", "Status", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_tenant_classes_tenant_id_status_sort_order_created_at"); - - b.ToTable("tenant_classes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("JoinedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("joined_at") - .HasDefaultValueSql("now()"); - - b.Property("LeftAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("left_at"); - - b.Property("MemberType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("member_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_class_members"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_class_members_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_class_members_user_id"); - - b.HasIndex("TenantId", "ClassId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_status_member_type"); - - b.HasIndex("TenantId", "ClassId", "UserId", "MemberType") - .IsUnique() - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_user_id_member_type"); - - b.HasIndex("TenantId", "UserId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_user_id_status_member_type"); - - b.ToTable("tenant_class_members", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DomainType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("domain_type"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(253) - .HasColumnType("citext") - .HasColumnName("host"); - - b.Property("IsPrimary") - .HasColumnType("boolean") - .HasColumnName("is_primary"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VerificationToken") - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("verification_token"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.HasKey("Id") - .HasName("pk_tenant_domains"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_domains_tenant_id_id"); - - b.HasIndex("Host") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_host"); - - b.HasIndex("TenantId", "IsPrimary") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_tenant_id_is_primary") - .HasFilter("is_primary"); - - b.ToTable("tenant_domains", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyRole") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("legacy_role"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Role") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("role"); - - b.Property("RoleTemplateId") - .HasColumnType("uuid") - .HasColumnName("role_template_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_memberships"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_memberships_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_memberships_user_id"); - - b.HasIndex("TenantId", "RoleTemplateId") - .HasDatabaseName("ix_tenant_memberships_tenant_id_role_template_id"); - - b.HasIndex("TenantId", "UserId", "Role") - .IsUnique() - .HasDatabaseName("ix_tenant_memberships_tenant_id_user_id_role"); - - b.ToTable("tenant_memberships", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("base_role"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DataScope") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("data_scope") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FieldPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IsSystem") - .HasColumnType("boolean") - .HasColumnName("is_system"); - - b.Property("MenuPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("menu_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModulePermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_role_templates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_role_templates_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_role_templates_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_role_templates_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_role_templates_tenant_id_code"); - - b.HasIndex("TenantId", "Status", "SortOrder") - .HasDatabaseName("ix_tenant_role_templates_tenant_id_status_sort_order"); - - b.ToTable("tenant_role_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Purpose") - .IsRequired() - .HasMaxLength(80) - .HasColumnType("character varying(80)") - .HasColumnName("purpose"); - - b.Property("RotatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rotated_at"); - - b.Property("SecretKey") - .IsRequired() - .HasMaxLength(120) - .HasColumnType("character varying(120)") - .HasColumnName("secret_key"); - - b.Property("SecretPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("secret_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecretRef") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_secrets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_secrets_tenant_id_id"); - - b.HasIndex("TenantId", "SecretRef") - .IsUnique() - .HasDatabaseName("ix_tenant_secrets_tenant_id_secret_ref"); - - b.HasIndex("TenantId", "Purpose", "Provider", "SecretKey") - .IsUnique() - .HasDatabaseName("ix_tenant_secrets_tenant_id_purpose_provider_secret_key"); - - b.HasIndex("TenantId", "Purpose", "Provider", "Status") - .HasDatabaseName("ix_tenant_secrets_tenant_id_purpose_provider_status"); - - b.ToTable("tenant_secrets", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("AdminFeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("admin_feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PublicConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_settings"); - - b.ToTable("tenant_settings", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CompletedBy") - .HasColumnType("uuid") - .HasColumnName("completed_by"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("FollowupType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("followup_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_student_followups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_followups_tenant_id_id"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_tenant_student_followups_assigned_to_user_id"); - - b.HasIndex("CompletedBy") - .HasDatabaseName("ix_tenant_student_followups_completed_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_followups_student_user_id"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_assigned_to_user_id_stat~") - .HasFilter("assigned_to_user_id is not null"); - - b.HasIndex("TenantId", "ClassId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_class_id_status_due_at") - .HasFilter("class_id is not null"); - - b.HasIndex("TenantId", "StudentUserId", "Status", "DueAt", "CreatedAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_student_user_id_status_d~"); - - b.ToTable("tenant_student_followups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("IsPinned") - .HasColumnType("boolean") - .HasColumnName("is_pinned"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NoteType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("note_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_tenant_student_notes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_notes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_student_notes_created_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_notes_student_user_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_student_notes_updated_by"); - - b.HasIndex("TenantId", "CreatedBy", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_created_by_created_at") - .HasFilter("created_by is not null"); - - b.HasIndex("TenantId", "StudentUserId", "IsPinned", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_student_user_id_is_pinned_cr~"); - - b.ToTable("tenant_student_notes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_categories_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_majors_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_module_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_module_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_regions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_region_modules_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_region_modules_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_schools_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineField", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_scoreline_fields_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_fields_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_scoreline_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AgentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_agent_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_activation_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UsedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_used_by"); - - b.HasOne("Tiku.Domain.Commerce.CodeBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_code_batches_tenant_id_batch_id"); - - b.HasOne("Tiku.Domain.Commerce.CouponRedemption", null) - .WithMany() - .HasForeignKey("TenantId", "CouponRedemptionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_coupon_redemptions_tenant_id_coupon_redemp~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "UsedRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_regions_tenant_id_used_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_code_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_batches_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedTo") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_assigned_to"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_resolved_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_batc~"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_item~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_refund_requests_ten~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issue_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationIssue", null) - .WithMany() - .HasForeignKey("TenantId", "IssueId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_commerce_reconciliatio~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_commerce_reconciliation_batch~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_commerce_refund_requests_tena~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_commerce_refund_requests_tenant_id_r~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ProcessedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_processed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RequestedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_requested_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_refund_requests_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupons_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_coupons_svip_plans_tenant_id_plan_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupon_redemptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.Coupon", null) - .WithMany() - .HasForeignKey("TenantId", "CouponId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_coupons_tenant_id_coupon_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RevokedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_entitlements_users_revoked_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_orders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_orders_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payment_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_payment_events_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityClaim", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_activity_claims_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_activity_claims_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.PointActivityTask", null) - .WithMany() - .HasForeignKey("TenantId", "TaskId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_point_activity_claims_point_activity_tasks_tenant_id_task_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityTask", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_activity_tasks_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_exchange_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_point_exchange_items_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeOrder", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_exchange_orders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_exchange_orders_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.PointExchangeItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_point_exchange_orders_point_exchange_items_tenant_id_item_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_products_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_products_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_svip_plans_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_svip_plans_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_payment_accounts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_subscriptions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_usage_records_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_ai_recommendation_reports_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_app_assets_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_assets_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("VerifiedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_verified_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_content_nodes_tenant_id_content_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_asset_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_access_events_content_assets_tenant_id_asset_~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_security_scan_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_security_scan_events_content_assets_tenant_id~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_entries_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_import_issues_content_import_items_tenant_id_item_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_import_jobs_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_jobs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_categories_tenant_id_target_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "TargetContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_content_nodes_tenant_id_target_content_~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_question_banks_tenant_id_target_questio~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "TargetRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_regions_tenant_id_target_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_nodes_content_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_chapters_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_chapters_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_handbook_chapters_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.HandbookSubject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_chapters_handbook_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.HandbookChapter", null) - .WithMany() - .HasForeignKey("TenantId", "ChapterId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_entries_handbook_chapters_tenant_id_chapter_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_entries_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_handbook_entries_content_entries_tenant_id_entry_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_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_handbook_subjects_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_images_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_blueprints_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_question_collections_tenant_id_collecti~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_bank_grants_question_banks_source_question_bank_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_question_banks_tenant_id_question_bank~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_question_collections_tenant_id_co~"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_type_groups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_type_groups_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_videos_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.Content.VideoExplanation", null) - .WithMany() - .HasForeignKey("TenantId", "VideoId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_video_explanations_tenant_id_video_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SourceSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_source_subject_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_created_by"); - - b.HasOne("Tiku.Domain.Content.QuestionBankGrant", null) - .WithMany() - .HasForeignKey("GrantId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_bank_grants_grant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_source_questi~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_collections_tenant_~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "TargetEntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_content_entries_tenant_id_ta~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_tenant_id_tar~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_video_explanations_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_video_explanations_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_units_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_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_vocabulary_units_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_words_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_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_vocabulary_words_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyUnit", null) - .WithMany() - .HasForeignKey("TenantId", "UnitId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_vocabulary_units_tenant_id_unit_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GeneratedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_generated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PaidBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_paid_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.CommissionSettlement", null) - .WithMany() - .HasForeignKey("TenantId", "SettlementId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_commission_settlements_tenant_i~"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_config_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_log_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_queue_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralTrack", null) - .WithMany() - .HasForeignKey("TenantId", "FirstTrackId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_referral_tracks_tenant_id_first_track_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_qrcodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_qrcodes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("LeaderUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_team_edges_users_leader_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("MemberUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_users_member_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("TargetUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_target_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_tracks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralLead", null) - .WithMany() - .HasForeignKey("TenantId", "LeadId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_referral_leads_tenant_id_lead_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_commission_settings_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_commission_settings_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedMajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_majors_tenant_id_selected_major_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedSchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_schools_tenant_id_selected_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_identities_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_pb_import_issues_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_runs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId", "QuestionVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_question_versions_tenant_id_question_id_ques~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "UserId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "UserId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_practice_sessions_tenant_id_user_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_dashboard_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_dashboard_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_exam_dates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_practice_sessions_tenant_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .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.PracticeSessionReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .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") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_practice_sessions_tenant_id_practi~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_sessions_tenant_i~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSessionReport", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_session_reports_t~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_recent_practices_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_recent_practices_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("HandledBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_handled_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_reports_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_report_status_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.Report", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_reports_tenant_id_report_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_revenue_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_revenue_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_announcements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_audit_logs_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_audit_logs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_badges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_banners_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_banners_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_faqs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_faqs_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReadBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_read_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_content_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "SourceQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_tenant_content_notifications_question_banks_tenant_id_sourc~"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("ActiveTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_active_template~"); - - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("DraftTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_draft_template_~"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("DraftUpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_draft_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PublishedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_published_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_theme_configs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GrantedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_badges_users_granted_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_badges_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_users_user_id"); - - b.HasOne("Tiku.Domain.Operations.Badge", null) - .WithMany() - .HasForeignKey("TenantId", "BadgeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_badges_tenant_id_badge_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AcknowledgedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_acknowledged_by"); - - b.HasOne("Tiku.Domain.Operations.AuditLog", null) - .WithMany() - .HasForeignKey("AuditLogId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_audit_logs_audit_log_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_resolved_by"); - - b.HasOne("Tiku.Domain.Platform.PlatformAuditAlertRule", null) - .WithMany() - .HasForeignKey("RuleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_platform_audit_alert_rules_rule_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alerts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alert_rules_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.HasOne("Tiku.Domain.Platform.PlatformDunningNotificationChannel", null) - .WithMany() - .HasForeignKey("ChannelId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_platform_dunning_notif~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoices_tenant~"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoiceReminder", null) - .WithMany() - .HasForeignKey("TenantId", "ReminderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoice_reminde~"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Platform.TenantBillingProfile", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_billing_profiles_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoices_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoices_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReceivedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_payments_users_received_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_reminders_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenant_invoices_tenant_id_invoice_~"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_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_questions_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "PrimaryCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_collections_tenant_id_primary_collection~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_banks_tenant_id_question_bank_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_subjects_tenant_id_subject_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "Id", "CurrentVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_versions_tenant_id_id_current_version_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_banks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_banks_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_versions_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_versions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_login_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_auth_login_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_send_rate_limits_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_verification_codes_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("OwnerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenants_users_owner_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_auth_providers_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantBranding", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_branding_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_classes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_classes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_domains_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantRoleTemplate", null) - .WithMany() - .HasForeignKey("TenantId", "RoleTemplateId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_role_templates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSecret", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_secrets_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantSettings", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_settings_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CompletedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_completed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_updated_by"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726115242_AddPointsPersistenceFoundation.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726115242_AddPointsPersistenceFoundation.cs deleted file mode 100644 index 35944c9..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726115242_AddPointsPersistenceFoundation.cs +++ /dev/null @@ -1,265 +0,0 @@ -using System; -using System.Text.Json; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - /// - public partial class AddPointsPersistenceFoundation : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "point_activity_tasks", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - task_key = table.Column(type: "citext", maxLength: 100, nullable: false), - title = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), - description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), - task_type = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - points = table.Column(type: "integer", nullable: false), - max_claims_per_user = table.Column(type: "integer", nullable: false), - starts_at = table.Column(type: "timestamp with time zone", nullable: true), - ends_at = table.Column(type: "timestamp with time zone", nullable: true), - sort_order = table.Column(type: "integer", nullable: false), - rules = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - tenant_id = table.Column(type: "uuid", nullable: false), - created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") - }, - constraints: table => - { - table.PrimaryKey("pk_point_activity_tasks", x => x.id); - table.UniqueConstraint("ak_point_activity_tasks_tenant_id_id", x => new { x.tenant_id, x.id }); - table.CheckConstraint("ck_point_activity_tasks_max_claims", "max_claims_per_user > 0"); - table.CheckConstraint("ck_point_activity_tasks_points", "points > 0"); - table.ForeignKey( - name: "fk_point_activity_tasks_tenants_tenant_id", - column: x => x.tenant_id, - principalTable: "tenants", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "point_exchange_items", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - region_id = table.Column(type: "uuid", nullable: true), - item_key = table.Column(type: "citext", maxLength: 100, nullable: false), - name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), - description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), - item_type = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - points_cost = table.Column(type: "integer", nullable: false), - stock = table.Column(type: "integer", nullable: true), - days = table.Column(type: "integer", nullable: true), - sort_order = table.Column(type: "integer", nullable: false), - starts_at = table.Column(type: "timestamp with time zone", nullable: true), - ends_at = table.Column(type: "timestamp with time zone", nullable: true), - fulfillment_payload = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - tenant_id = table.Column(type: "uuid", nullable: false), - created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") - }, - constraints: table => - { - table.PrimaryKey("pk_point_exchange_items", x => x.id); - table.UniqueConstraint("ak_point_exchange_items_tenant_id_id", x => new { x.tenant_id, x.id }); - table.CheckConstraint("ck_point_exchange_items_days", "days is null or days >= 0"); - table.CheckConstraint("ck_point_exchange_items_points_cost", "points_cost > 0"); - table.CheckConstraint("ck_point_exchange_items_stock", "stock is null or stock >= 0"); - table.ForeignKey( - name: "fk_point_exchange_items_regions_tenant_id_region_id", - columns: x => new { x.tenant_id, x.region_id }, - principalTable: "regions", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_point_exchange_items_tenants_tenant_id", - column: x => x.tenant_id, - principalTable: "tenants", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "point_activity_claims", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - task_id = table.Column(type: "uuid", nullable: false), - user_id = table.Column(type: "uuid", nullable: false), - task_key = table.Column(type: "citext", maxLength: 100, nullable: false), - points = table.Column(type: "integer", nullable: false), - status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - source_type = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), - source_id = table.Column(type: "uuid", nullable: true), - claimed_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - tenant_id = table.Column(type: "uuid", nullable: false), - created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") - }, - constraints: table => - { - table.PrimaryKey("pk_point_activity_claims", x => x.id); - table.UniqueConstraint("ak_point_activity_claims_tenant_id_id", x => new { x.tenant_id, x.id }); - table.CheckConstraint("ck_point_activity_claims_points", "points > 0"); - table.ForeignKey( - name: "fk_point_activity_claims_point_activity_tasks_tenant_id_task_id", - columns: x => new { x.tenant_id, x.task_id }, - principalTable: "point_activity_tasks", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_point_activity_claims_tenants_tenant_id", - column: x => x.tenant_id, - principalTable: "tenants", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "fk_point_activity_claims_users_user_id", - column: x => x.user_id, - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "point_exchange_orders", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - item_id = table.Column(type: "uuid", nullable: false), - user_id = table.Column(type: "uuid", nullable: false), - order_no = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), - item_name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), - item_type = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - points_cost = table.Column(type: "integer", nullable: false), - ordered_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - completed_at = table.Column(type: "timestamp with time zone", nullable: true), - cancelled_at = table.Column(type: "timestamp with time zone", nullable: true), - fulfillment_snapshot = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - tenant_id = table.Column(type: "uuid", nullable: false), - created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") - }, - constraints: table => - { - table.PrimaryKey("pk_point_exchange_orders", x => x.id); - table.UniqueConstraint("ak_point_exchange_orders_tenant_id_id", x => new { x.tenant_id, x.id }); - table.CheckConstraint("ck_point_exchange_orders_points_cost", "points_cost > 0"); - table.ForeignKey( - name: "fk_point_exchange_orders_point_exchange_items_tenant_id_item_id", - columns: x => new { x.tenant_id, x.item_id }, - principalTable: "point_exchange_items", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_point_exchange_orders_tenants_tenant_id", - column: x => x.tenant_id, - principalTable: "tenants", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "fk_point_exchange_orders_users_user_id", - column: x => x.user_id, - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "ix_point_activity_claims_tenant_id_task_id", - table: "point_activity_claims", - columns: new[] { "tenant_id", "task_id" }); - - migrationBuilder.CreateIndex( - name: "ix_point_activity_claims_tenant_id_user_id_created_at", - table: "point_activity_claims", - columns: new[] { "tenant_id", "user_id", "created_at" }); - - migrationBuilder.CreateIndex( - name: "ix_point_activity_claims_tenant_id_user_id_task_id_source_type~", - table: "point_activity_claims", - columns: new[] { "tenant_id", "user_id", "task_id", "source_type", "source_id" }, - unique: true, - filter: "source_type is not null and source_id is not null"); - - migrationBuilder.CreateIndex( - name: "ix_point_activity_claims_user_id", - table: "point_activity_claims", - column: "user_id"); - - migrationBuilder.CreateIndex( - name: "ix_point_activity_tasks_tenant_id_status_sort_order", - table: "point_activity_tasks", - columns: new[] { "tenant_id", "status", "sort_order" }); - - migrationBuilder.CreateIndex( - name: "ix_point_activity_tasks_tenant_id_task_key", - table: "point_activity_tasks", - columns: new[] { "tenant_id", "task_key" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "ix_point_exchange_items_tenant_id_item_key", - table: "point_exchange_items", - columns: new[] { "tenant_id", "item_key" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "ix_point_exchange_items_tenant_id_region_id_status_sort_order", - table: "point_exchange_items", - columns: new[] { "tenant_id", "region_id", "status", "sort_order" }); - - migrationBuilder.CreateIndex( - name: "ix_point_exchange_orders_tenant_id_item_id", - table: "point_exchange_orders", - columns: new[] { "tenant_id", "item_id" }); - - migrationBuilder.CreateIndex( - name: "ix_point_exchange_orders_tenant_id_order_no", - table: "point_exchange_orders", - columns: new[] { "tenant_id", "order_no" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "ix_point_exchange_orders_tenant_id_user_id_created_at", - table: "point_exchange_orders", - columns: new[] { "tenant_id", "user_id", "created_at" }); - - migrationBuilder.CreateIndex( - name: "ix_point_exchange_orders_user_id", - table: "point_exchange_orders", - column: "user_id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "point_activity_claims"); - - migrationBuilder.DropTable( - name: "point_exchange_orders"); - - migrationBuilder.DropTable( - name: "point_activity_tasks"); - - migrationBuilder.DropTable( - name: "point_exchange_items"); - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726122712_AddCommissionSettlementProofs.Designer.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726122712_AddCommissionSettlementProofs.Designer.cs deleted file mode 100644 index bf0046c..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726122712_AddCommissionSettlementProofs.Designer.cs +++ /dev/null @@ -1,16416 +0,0 @@ -// -using System; -using System.Text.Json; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using Tiku.Infrastructure.Persistence; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - [DbContext(typeof(TikuDbContext))] - [Migration("20260726122712_AddCommissionSettlementProofs")] - partial class AddCommissionSettlementProofs - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext"); - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "ltree"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("category_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("SvipQuestionLimit") - .HasColumnType("integer") - .HasColumnName("svip_question_limit"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_categories"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_categories_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_categories_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_categories_tenant_id_node_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_categories_tenant_id_subject_id"); - - b.ToTable("categories", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("StudyTips") - .HasColumnType("text") - .HasColumnName("study_tips"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_majors"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_majors_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_majors_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_majors_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_majors_tenant_id_school_id"); - - b.ToTable("majors", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyModuleId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_module_id"); - - b.Property("LegacyParentId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_parent_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_module_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_module_nodes_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_module_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_module_nodes_tenant_id_module_id"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_module_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_module_nodes_tenant_id_region_id"); - - b.ToTable("module_nodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FullName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("full_name"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsHot") - .HasColumnType("boolean") - .HasColumnName("is_hot"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Pinyin") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("pinyin"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_regions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_regions_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_regions_tenant_id_legacy_id"); - - b.ToTable("regions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsPrimarySchoolModule") - .HasColumnType("boolean") - .HasColumnName("is_primary_school_module"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TextColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("text_color"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_region_modules"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_region_modules_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_region_modules_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_region_modules_tenant_id_region_id"); - - b.ToTable("region_modules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("ProfessionalExamDate") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("professional_exam_date"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_schools"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_schools_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_schools_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_schools_tenant_id_module_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_schools_tenant_id_region_id"); - - b.ToTable("schools", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineField", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FieldKey") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("field_key"); - - b.Property("FieldName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("field_name"); - - b.Property("FieldType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("field_type"); - - b.Property("IsFilter") - .HasColumnType("boolean") - .HasColumnName("is_filter"); - - b.Property("IsRequired") - .HasColumnType("boolean") - .HasColumnName("is_required"); - - b.Property("IsTrend") - .HasColumnType("boolean") - .HasColumnName("is_trend"); - - b.Property("IsVisible") - .HasColumnType("boolean") - .HasColumnName("is_visible"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Placeholder") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("placeholder"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Unit") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("unit"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_scoreline_fields"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_scoreline_fields_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_scoreline_fields_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "FieldKey") - .IsUnique() - .HasDatabaseName("ix_scoreline_fields_tenant_id_region_id_field_key"); - - b.HasIndex("TenantId", "RegionId", "IsFilter", "SortOrder") - .HasDatabaseName("ix_scoreline_fields_tenant_id_region_id_is_filter_sort_order"); - - b.ToTable("scoreline_fields", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldValues") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_values") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("major_name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SchoolName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("school_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Year") - .HasColumnType("integer") - .HasColumnName("year"); - - b.HasKey("Id") - .HasName("pk_scoreline_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_scoreline_records_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_scoreline_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_scoreline_records_tenant_id_major_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_scoreline_records_tenant_id_school_id"); - - b.HasIndex("TenantId", "Year") - .HasDatabaseName("ix_scoreline_records_tenant_id_year"); - - b.HasIndex("TenantId", "RegionId", "SchoolId", "MajorId", "Year") - .HasDatabaseName("ix_scoreline_records_tenant_id_region_id_school_id_major_id_ye~"); - - b.ToTable("scoreline_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ModuleId") - .HasColumnType("uuid") - .HasColumnName("module_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "ModuleId") - .HasDatabaseName("ix_subjects_tenant_id_module_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_subjects_tenant_id_node_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_subjects_tenant_id_school_id"); - - b.ToTable("subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AgentUserId") - .HasColumnType("uuid") - .HasColumnName("agent_user_id"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponRedemptionId") - .HasColumnType("uuid") - .HasColumnName("coupon_redemption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("IsUsed") - .HasColumnType("boolean") - .HasColumnName("is_used"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("SoldTo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("sold_to"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitPriceCents") - .HasColumnType("integer") - .HasColumnName("unit_price_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UsedBy") - .HasColumnType("uuid") - .HasColumnName("used_by"); - - b.Property("UsedRegionId") - .HasColumnType("uuid") - .HasColumnName("used_region_id"); - - b.HasKey("Id") - .HasName("pk_activation_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_activation_codes_tenant_id_id"); - - b.HasIndex("AgentUserId") - .HasDatabaseName("ix_activation_codes_agent_user_id"); - - b.HasIndex("UsedBy") - .HasDatabaseName("ix_activation_codes_used_by"); - - b.HasIndex("TenantId", "BatchId") - .HasDatabaseName("ix_activation_codes_tenant_id_batch_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_code"); - - b.HasIndex("TenantId", "CouponRedemptionId") - .HasDatabaseName("ix_activation_codes_tenant_id_coupon_redemption_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_activation_codes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UsedRegionId") - .HasDatabaseName("ix_activation_codes_tenant_id_used_region_id"); - - b.ToTable("activation_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CampaignName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("campaign_name"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CostPriceCents") - .HasColumnType("integer") - .HasColumnName("cost_price_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("DefaultUnitPriceCents") - .HasColumnType("integer") - .HasColumnName("default_unit_price_cents"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_code_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_code_batches_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_code_batches_tenant_id_legacy_id"); - - b.ToTable("code_batches", null, t => - { - t.HasCheckConstraint("ck_code_batches_amounts", "default_unit_price_cents >= 0 and cost_price_cents >= 0"); - - t.HasCheckConstraint("ck_code_batches_counts", "total_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillDate") - .HasColumnType("date") - .HasColumnName("bill_date"); - - b.Property("BillType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bill_type"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DuplicateCount") - .HasColumnType("integer") - .HasColumnName("duplicate_count"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IgnoredCount") - .HasColumnType("integer") - .HasColumnName("ignored_count"); - - b.Property("MatchedCount") - .HasColumnType("integer") - .HasColumnName("matched_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MismatchCount") - .HasColumnType("integer") - .HasColumnName("mismatch_count"); - - b.Property("MissingLocalCount") - .HasColumnType("integer") - .HasColumnName("missing_local_count"); - - b.Property("MissingProviderCount") - .HasColumnType("integer") - .HasColumnName("missing_provider_count"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source"); - - b.Property("SourceHash") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("source_name"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_batches"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_batches_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_batches_created_by"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "Provider", "BillDate", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_batches_tenant_id_provider_bill_dat~"); - - b.ToTable("commerce_reconciliation_batches", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_batches_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_batches_counts", "total_count >= 0 and matched_count >= 0 and mismatch_count >= 0 and missing_local_count >= 0 and missing_provider_count >= 0 and duplicate_count >= 0 and ignored_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("AssignedTo") - .HasColumnType("uuid") - .HasColumnName("assigned_to"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("IssueNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_no"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolutionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("resolution_type"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("summary"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issues_tenant_id_id"); - - b.HasIndex("AssignedTo") - .HasDatabaseName("ix_commerce_reconciliation_issues_assigned_to"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_created_by"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_commerce_reconciliation_issues_resolved_by"); - - b.HasIndex("TenantId", "IssueNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_issue_no"); - - b.HasIndex("TenantId", "ItemId") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_item_id") - .HasFilter("item_id is not null and status in ('open', 'investigating', 'escalated')"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "PaymentId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_payment_id_created~"); - - b.HasIndex("TenantId", "AssignedTo", "Status", "DueAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_assigned_to_status~"); - - b.HasIndex("TenantId", "BatchId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_batch_id_status_cr~"); - - b.HasIndex("TenantId", "Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issues_tenant_id_status_severity_cr~"); - - b.ToTable("commerce_reconciliation_issues", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_issues_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("IssueId") - .HasColumnType("uuid") - .HasColumnName("issue_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_issue_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_issue_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_actor_user_id"); - - b.HasIndex("TenantId", "IssueId", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_issue_events_tenant_id_issue_id_cre~"); - - b.ToTable("commerce_reconciliation_issue_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BatchId") - .HasColumnType("uuid") - .HasColumnName("batch_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FeeCents") - .HasColumnType("integer") - .HasColumnName("fee_cents"); - - b.Property("IssueCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LocalStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("local_status"); - - b.Property("MatchStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("match_status"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("OrderNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("ProviderStatus") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider_status"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RefundAmountCents") - .HasColumnType("integer") - .HasColumnName("refund_amount_cents"); - - b.Property("RefundNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("RefundedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("refunded_at"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TransactionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("transaction_type"); - - b.HasKey("Id") - .HasName("pk_commerce_reconciliation_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_reconciliation_items_tenant_id_id"); - - b.HasIndex("BatchId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_items_batch_id_row_no"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundRequestId") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_request_id"); - - b.HasIndex("TenantId", "OrderNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_no_created_at"); - - b.HasIndex("TenantId", "BatchId", "MatchStatus", "RowNo") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_batch_id_match_stat~"); - - b.HasIndex("TenantId", "Provider", "ProviderTradeNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_provider_provider_t~"); - - b.HasIndex("TenantId", "RefundNo", "ProviderRefundNo", "CreatedAt") - .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_no_provider_~"); - - b.ToTable("commerce_reconciliation_items", null, t => - { - t.HasCheckConstraint("ck_commerce_reconciliation_items_amounts", "amount_cents >= 0 and refund_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commerce_reconciliation_items_row", "row_no > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("RefundRequestId") - .HasColumnType("uuid") - .HasColumnName("refund_request_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_commerce_refund_events_actor_user_id"); - - b.HasIndex("TenantId", "RefundRequestId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_events_tenant_id_refund_request_id_created_~"); - - b.ToTable("commerce_refund_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CancelledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("cancelled_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementAction") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entitlement_action"); - - b.Property("FailedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("failed_at"); - - b.Property("FailureReason") - .HasColumnType("text") - .HasColumnName("failure_reason"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("ProcessedBy") - .HasColumnType("uuid") - .HasColumnName("processed_by"); - - b.Property("Provider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderRefundNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_refund_no"); - - b.Property("Reason") - .HasColumnType("text") - .HasColumnName("reason"); - - b.Property("RefundNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("refund_no"); - - b.Property("RequestedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("requested_at") - .HasDefaultValueSql("now()"); - - b.Property("RequestedBy") - .HasColumnType("uuid") - .HasColumnName("requested_by"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SucceededAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("succeeded_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commerce_refund_requests"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commerce_refund_requests_tenant_id_id"); - - b.HasIndex("ProcessedBy") - .HasDatabaseName("ix_commerce_refund_requests_processed_by"); - - b.HasIndex("RequestedBy") - .HasDatabaseName("ix_commerce_refund_requests_requested_by"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commerce_refund_requests_reviewed_by"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_payment_id"); - - b.HasIndex("TenantId", "RefundNo") - .IsUnique() - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_refund_no"); - - b.HasIndex("TenantId", "OrderId", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_order_id_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_commerce_refund_requests_tenant_id_status_created_at"); - - b.ToTable("commerce_refund_requests", null, t => - { - t.HasCheckConstraint("ck_commerce_refund_requests_amount", "amount_cents > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("discount_type"); - - b.Property("DiscountValue") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("discount_value"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MaxUses") - .HasColumnType("integer") - .HasColumnName("max_uses"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("ValidFrom") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_from"); - - b.Property("ValidTo") - .HasColumnType("timestamp with time zone") - .HasColumnName("valid_to"); - - b.HasKey("Id") - .HasName("pk_coupons"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupons_tenant_id_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_code"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupons_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupons_tenant_id_plan_id"); - - b.ToTable("coupons", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClaimedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("claimed_at"); - - b.Property("CouponCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("coupon_code"); - - b.Property("CouponId") - .HasColumnType("uuid") - .HasColumnName("coupon_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DiscountAppliedCents") - .HasColumnType("integer") - .HasColumnName("discount_applied_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("Source") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("used_at"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_coupon_redemptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_coupon_redemptions_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_coupon_redemptions_user_id"); - - b.HasIndex("TenantId", "CouponId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_coupon_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_region_id"); - - b.HasIndex("TenantId", "OrderId", "UserId") - .HasDatabaseName("ix_coupon_redemptions_tenant_id_order_id_user_id") - .HasFilter("order_id is not null"); - - b.HasIndex("TenantId", "UserId", "CouponId") - .IsUnique() - .HasDatabaseName("ix_coupon_redemptions_tenant_id_user_id_coupon_id") - .HasFilter("coupon_id is not null"); - - b.ToTable("coupon_redemptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("entitlement_type"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("LegacySourceId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_source_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("RevokedBy") - .HasColumnType("uuid") - .HasColumnName("revoked_by"); - - b.Property("RevokedReason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("revoked_reason"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("StartsAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at") - .HasDefaultValueSql("now()"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_entitlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_entitlements_tenant_id_id"); - - b.HasIndex("RevokedBy") - .HasDatabaseName("ix_entitlements_revoked_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_entitlements_user_id"); - - b.HasIndex("TenantId", "UserId", "Status", "ExpiresAt") - .HasDatabaseName("ix_entitlements_tenant_id_user_id_status_expires_at"); - - b.ToTable("entitlements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPlanId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_plan_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("OrderNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PayMethod") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_method"); - - b.Property("PayProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("pay_provider"); - - b.Property("PlanId") - .HasColumnType("uuid") - .HasColumnName("plan_id"); - - b.Property("ProductName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("product_name"); - - b.Property("ProductType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("product_type"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("trade_no"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_orders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_orders_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_orders_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderNo") - .IsUnique() - .HasDatabaseName("ix_orders_tenant_id_order_no"); - - b.HasIndex("TenantId", "PlanId") - .HasDatabaseName("ix_orders_tenant_id_plan_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_orders_tenant_id_region_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_orders_tenant_id_user_id_created_at"); - - b.ToTable("orders", null, t => - { - t.HasCheckConstraint("ck_orders_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_orders_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("Quantity") - .HasColumnType("integer") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalAmountCents") - .HasColumnType("integer") - .HasColumnName("total_amount_cents"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_order_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_order_items_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_order_items_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId") - .HasDatabaseName("ix_order_items_tenant_id_order_id"); - - b.ToTable("order_items", null, t => - { - t.HasCheckConstraint("ck_order_items_amounts", "unit_amount_cents >= 0 and total_amount_cents >= 0"); - - t.HasCheckConstraint("ck_order_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyOrderId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_order_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("OrderId") - .HasColumnType("uuid") - .HasColumnName("order_id"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefundedAmountCents") - .HasColumnType("integer") - .HasColumnName("refunded_amount_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payments_tenant_id_id"); - - b.HasIndex("Provider", "ProviderTradeNo") - .IsUnique() - .HasDatabaseName("ix_payments_provider_provider_trade_no") - .HasFilter("provider_trade_no is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_payments_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "OrderId", "UpdatedAt") - .HasDatabaseName("ix_payments_tenant_id_order_id_updated_at"); - - b.ToTable("payments", null, t => - { - t.HasCheckConstraint("ck_payments_amount", "amount_cents >= 0"); - - t.HasCheckConstraint("ck_payments_refunded_amount", "refunded_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Error") - .HasColumnType("text") - .HasColumnName("error"); - - b.Property("EventId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("event_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaymentId") - .HasColumnType("uuid") - .HasColumnName("payment_id"); - - b.Property("ProcessedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("processed_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("SignatureValid") - .HasColumnType("boolean") - .HasColumnName("signature_valid"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_payment_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_payment_events_tenant_id_id"); - - b.HasIndex("Provider", "EventId") - .IsUnique() - .HasDatabaseName("ix_payment_events_provider_event_id"); - - b.HasIndex("TenantId", "PaymentId") - .HasDatabaseName("ix_payment_events_tenant_id_payment_id"); - - b.ToTable("payment_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClaimedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("claimed_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Points") - .HasColumnType("integer") - .HasColumnName("points"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TaskId") - .HasColumnType("uuid") - .HasColumnName("task_id"); - - b.Property("TaskKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("task_key"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_point_activity_claims"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_point_activity_claims_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_point_activity_claims_user_id"); - - b.HasIndex("TenantId", "TaskId") - .HasDatabaseName("ix_point_activity_claims_tenant_id_task_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_point_activity_claims_tenant_id_user_id_created_at"); - - b.HasIndex("TenantId", "UserId", "TaskId", "SourceType", "SourceId") - .IsUnique() - .HasDatabaseName("ix_point_activity_claims_tenant_id_user_id_task_id_source_type~") - .HasFilter("source_type is not null and source_id is not null"); - - b.ToTable("point_activity_claims", null, t => - { - t.HasCheckConstraint("ck_point_activity_claims_points", "points > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityTask", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("EndsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("ends_at"); - - b.Property("MaxClaimsPerUser") - .HasColumnType("integer") - .HasColumnName("max_claims_per_user"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Points") - .HasColumnType("integer") - .HasColumnName("points"); - - b.Property("Rules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TaskKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("task_key"); - - b.Property("TaskType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("task_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_point_activity_tasks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_point_activity_tasks_tenant_id_id"); - - b.HasIndex("TenantId", "TaskKey") - .IsUnique() - .HasDatabaseName("ix_point_activity_tasks_tenant_id_task_key"); - - b.HasIndex("TenantId", "Status", "SortOrder") - .HasDatabaseName("ix_point_activity_tasks_tenant_id_status_sort_order"); - - b.ToTable("point_activity_tasks", null, t => - { - t.HasCheckConstraint("ck_point_activity_tasks_max_claims", "max_claims_per_user > 0"); - - t.HasCheckConstraint("ck_point_activity_tasks_points", "points > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("EndsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("ends_at"); - - b.Property("FulfillmentPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("fulfillment_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ItemKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("item_key"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("item_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PointsCost") - .HasColumnType("integer") - .HasColumnName("points_cost"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Stock") - .HasColumnType("integer") - .HasColumnName("stock"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_point_exchange_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_point_exchange_items_tenant_id_id"); - - b.HasIndex("TenantId", "ItemKey") - .IsUnique() - .HasDatabaseName("ix_point_exchange_items_tenant_id_item_key"); - - b.HasIndex("TenantId", "RegionId", "Status", "SortOrder") - .HasDatabaseName("ix_point_exchange_items_tenant_id_region_id_status_sort_order"); - - b.ToTable("point_exchange_items", null, t => - { - t.HasCheckConstraint("ck_point_exchange_items_days", "days is null or days >= 0"); - - t.HasCheckConstraint("ck_point_exchange_items_points_cost", "points_cost > 0"); - - t.HasCheckConstraint("ck_point_exchange_items_stock", "stock is null or stock >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeOrder", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CancelledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("cancelled_at"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FulfillmentSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("fulfillment_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("ItemName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("item_name"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("item_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("OrderNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("order_no"); - - b.Property("OrderedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("ordered_at") - .HasDefaultValueSql("now()"); - - b.Property("PointsCost") - .HasColumnType("integer") - .HasColumnName("points_cost"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_point_exchange_orders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_point_exchange_orders_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_point_exchange_orders_user_id"); - - b.HasIndex("TenantId", "ItemId") - .HasDatabaseName("ix_point_exchange_orders_tenant_id_item_id"); - - b.HasIndex("TenantId", "OrderNo") - .IsUnique() - .HasDatabaseName("ix_point_exchange_orders_tenant_id_order_no"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_point_exchange_orders_tenant_id_user_id_created_at"); - - b.ToTable("point_exchange_orders", null, t => - { - t.HasCheckConstraint("ck_point_exchange_orders_points_cost", "points_cost > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Cover") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cover"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DetailImages") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("detail_images") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true) - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPriceText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_price_text"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("PreviewIframe") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_iframe"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_products"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_products_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_products_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "Type", "IsActive", "SortOrder") - .HasDatabaseName("ix_products_tenant_id_region_id_type_is_active_sort_order"); - - b.ToTable("products", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Badge") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("badge"); - - b.Property("CouponOnly") - .HasColumnType("boolean") - .HasColumnName("coupon_only"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Days") - .HasColumnType("integer") - .HasColumnName("days"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OriginalPriceCents") - .HasColumnType("integer") - .HasColumnName("original_price_cents"); - - b.Property("PerDayLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("per_day_label"); - - b.Property("PriceCents") - .HasColumnType("integer") - .HasColumnName("price_cents"); - - b.Property("Recommended") - .HasColumnType("boolean") - .HasColumnName("recommended"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VpEnabled") - .HasColumnType("boolean") - .HasColumnName("vp_enabled"); - - b.Property("VpProductId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("vp_product_id"); - - b.HasKey("Id") - .HasName("pk_svip_plans"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_svip_plans_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_svip_plans_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_svip_plans_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("svip_plans", null, t => - { - t.HasCheckConstraint("ck_svip_plans_days", "days >= 0"); - - t.HasCheckConstraint("ck_svip_plans_price", "price_cents >= 0 and (original_price_cents is null or original_price_cents >= 0)"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_payment_accounts"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_payment_accounts_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_payment_accounts_tenant_id_provider"); - - b.ToTable("tenant_payment_accounts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("BillingCycle") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("billing_cycle"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PlanCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("plan_code"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_subscriptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_subscriptions_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "ExpiresAt") - .HasDatabaseName("ix_tenant_subscriptions_tenant_id_status_expires_at"); - - b.ToTable("tenant_subscriptions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MetricKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("metric_key"); - - b.Property("MetricValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("metric_value"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_tenant_usage_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_usage_records_tenant_id_id"); - - b.HasIndex("TenantId", "MetricKey", "PeriodStart", "PeriodEnd") - .HasDatabaseName("ix_tenant_usage_records_tenant_id_metric_key_period_start_peri~"); - - b.ToTable("tenant_usage_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContextPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("context_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("GeneratedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("generated_at"); - - b.Property("InputPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("input_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Model") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("model"); - - b.Property("PromptVersion") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("prompt_version"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("ResultPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("result_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_ai_recommendation_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_ai_recommendation_reports_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_ai_recommendation_reports_user_id"); - - b.HasIndex("TenantId", "RegionId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_region_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_ai_recommendation_reports_tenant_id_user_id_created_at"); - - b.ToTable("ai_recommendation_reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetKey") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("description"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_app_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_app_assets_tenant_id_id"); - - b.HasIndex("TenantId", "AssetKey") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_asset_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_app_assets_tenant_id_legacy_id"); - - b.ToTable("app_assets", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssetKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("asset_key"); - - b.Property("AssetType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("asset_type"); - - b.Property("Bucket") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bucket"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("ChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("checksum_sha256"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DownloadCount") - .HasColumnType("integer") - .HasColumnName("download_count"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("FileSizeBytes") - .HasColumnType("bigint") - .HasColumnName("file_size_bytes"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MimeType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mime_type"); - - b.Property("ObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("object_key"); - - b.Property("PreviewObjectKey") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("preview_object_key"); - - b.Property("PreviewStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("preview_status"); - - b.Property("PreviewUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_url"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SecurityFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScanProvider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("security_scan_provider"); - - b.Property("SecurityScanStatus") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("not_required") - .HasColumnName("security_scan_status"); - - b.Property("SecurityScanSummary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("security_scan_summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecurityScannedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("security_scanned_at"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StorageProvider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("storage_provider"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UploadStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("upload_status"); - - b.Property("VerificationDetails") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("verification_details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.Property("VerifiedBy") - .HasColumnType("uuid") - .HasColumnName("verified_by"); - - b.Property("VerifiedChecksumSha256") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("verified_checksum_sha256"); - - b.Property("VerifiedSizeBytes") - .HasColumnType("bigint") - .HasColumnName("verified_size_bytes"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_assets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_assets_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_assets_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_assets_updated_by"); - - b.HasIndex("VerifiedBy") - .HasDatabaseName("ix_content_assets_verified_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_content_assets_tenant_id_category_id"); - - b.HasIndex("TenantId", "ChecksumSha256") - .HasDatabaseName("ix_content_assets_tenant_id_checksum_sha256") - .HasFilter("checksum_sha256 is not null"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_content_assets_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_assets_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "PreviewStatus") - .HasDatabaseName("ix_content_assets_tenant_id_preview_status") - .HasFilter("preview_status <> 'none'"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_assets_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_content_assets_tenant_id_subject_id"); - - b.HasIndex("TenantId", "StorageProvider", "Bucket", "ObjectKey") - .HasDatabaseName("ix_content_assets_tenant_id_storage_provider_bucket_object_key"); - - b.HasIndex("TenantId", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_upload_status_status_updated_at"); - - b.HasIndex("TenantId", "SecurityScanStatus", "UploadStatus", "Status", "UpdatedAt") - .HasDatabaseName("ix_content_assets_tenant_id_security_scan_status_upload_status~"); - - b.HasIndex("TenantId", "Status", "Visibility", "AssetType", "RegionId", "SubjectId", "CategoryId", "SortOrder") - .HasDatabaseName("ix_content_assets_tenant_id_status_visibility_asset_type_regio~"); - - b.ToTable("content_assets", null, t => - { - t.HasCheckConstraint("ck_content_assets_download_count", "download_count >= 0"); - - t.HasCheckConstraint("ck_content_assets_file_size", "file_size_bytes is null or file_size_bytes >= 0"); - - t.HasCheckConstraint("ck_content_assets_verified_checksum", "verified_checksum_sha256 is null or verified_checksum_sha256 ~ '^[a-f0-9]{64}$'"); - - t.HasCheckConstraint("ck_content_assets_verified_size", "verified_size_bytes is null or verified_size_bytes >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_type"); - - b.Property("ActorRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("actor_role"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("AssetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("asset_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DenyCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("deny_code"); - - b.Property("Disposition") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("disposition"); - - b.Property("ExpiresInSeconds") - .HasColumnType("integer") - .HasColumnName("expires_in_seconds"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("SignatureMode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("signature_mode"); - - b.Property("StorageProvider") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("storage_provider"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("Visibility") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_asset_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_content_asset_access_events_user_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_asset_id_created_at"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_user_id_created_at") - .HasFilter("user_id is not null"); - - b.HasIndex("TenantId", "AccessType", "Result", "CreatedAt") - .HasDatabaseName("ix_content_asset_access_events_tenant_id_access_type_result_cr~"); - - b.ToTable("content_asset_access_events", null, t => - { - t.HasCheckConstraint("ck_content_asset_access_events_expiry", "expires_in_seconds is null or expires_in_seconds > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.PrimitiveCollection("IssueCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("issue_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RiskLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("risk_level"); - - b.Property("ScanStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scan_status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_content_asset_security_scan_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_asset_security_scan_events_tenant_id_id"); - - b.HasIndex("TenantId", "AssetId", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_asset_id_creat~"); - - b.HasIndex("TenantId", "ScanStatus", "RiskLevel", "CreatedAt") - .HasDatabaseName("ix_content_asset_security_scan_events_tenant_id_scan_status_ri~"); - - b.ToTable("content_asset_security_scan_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("description"); - - b.Property("EntryKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("entry_key"); - - b.Property("EntryType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("entry_type"); - - b.Property("Icon") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LayoutConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("layout_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Route") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("route"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_content_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_entries_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_entries_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_entries_updated_by"); - - b.HasIndex("TenantId", "EntryKey") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_entry_key"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "EntryType", "IsActive", "SortOrder") - .HasDatabaseName("ix_content_entries_tenant_id_region_id_entry_type_is_active_so~"); - - b.ToTable("content_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("ItemId") - .HasColumnType("uuid") - .HasColumnName("item_id"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("Message") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("message"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "ItemId") - .HasDatabaseName("ix_content_import_issues_tenant_id_item_id"); - - b.HasIndex("TenantId", "JobId", "Severity", "RowNo") - .HasDatabaseName("ix_content_import_issues_tenant_id_job_id_severity_row_no"); - - b.ToTable("content_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("content_hash"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExternalId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("external_id"); - - b.Property("IssuesCount") - .HasColumnType("integer") - .HasColumnName("issues_count"); - - b.Property("JobId") - .HasColumnType("uuid") - .HasColumnName("job_id"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RowNo") - .HasColumnType("integer") - .HasColumnName("row_no"); - - b.Property("SourcePayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_content_import_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_items_tenant_id_id"); - - b.HasIndex("JobId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_content_import_items_job_id_row_no"); - - b.HasIndex("TenantId", "JobId", "Status", "RowNo") - .HasDatabaseName("ix_content_import_items_tenant_id_job_id_status_row_no"); - - b.ToTable("content_import_items", null, t => - { - t.HasCheckConstraint("ck_content_import_items_issues_count", "issues_count >= 0"); - - t.HasCheckConstraint("ck_content_import_items_row_no", "row_no >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DryRun") - .HasColumnType("boolean") - .HasColumnName("dry_run"); - - b.Property("ErrorCount") - .HasColumnType("integer") - .HasColumnName("error_count"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("ImportType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("import_type"); - - b.Property("InsertedCount") - .HasColumnType("integer") - .HasColumnName("inserted_count"); - - b.Property("NormalizedPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("normalized_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SkippedCount") - .HasColumnType("integer") - .HasColumnName("skipped_count"); - - b.Property("SourceFormat") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_format"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SourceName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("summary") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TargetCategoryId") - .HasColumnType("uuid") - .HasColumnName("target_category_id"); - - b.Property("TargetContentNodeId") - .HasColumnType("uuid") - .HasColumnName("target_content_node_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TargetRegionId") - .HasColumnType("uuid") - .HasColumnName("target_region_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCount") - .HasColumnType("integer") - .HasColumnName("total_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedCount") - .HasColumnType("integer") - .HasColumnName("updated_count"); - - b.Property("ValidCount") - .HasColumnType("integer") - .HasColumnName("valid_count"); - - b.Property("WarningCount") - .HasColumnType("integer") - .HasColumnName("warning_count"); - - b.HasKey("Id") - .HasName("pk_content_import_jobs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_import_jobs_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_import_jobs_created_by"); - - b.HasIndex("TenantId", "TargetCategoryId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_category_id"); - - b.HasIndex("TenantId", "TargetContentNodeId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_content_node_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_question_bank_id"); - - b.HasIndex("TenantId", "TargetRegionId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_region_id"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_content_import_jobs_tenant_id_target_subject_id"); - - b.HasIndex("TenantId", "ImportType", "Status", "CreatedAt") - .HasDatabaseName("ix_content_import_jobs_tenant_id_import_type_status_created_at"); - - b.ToTable("content_import_jobs", null, t => - { - t.HasCheckConstraint("ck_content_import_jobs_counts", "total_count >= 0 and valid_count >= 0 and error_count >= 0 and warning_count >= 0 and inserted_count >= 0 and updated_count >= 0 and skipped_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Depth") - .HasColumnType("integer") - .HasColumnName("depth"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsLeaf") - .HasColumnType("boolean") - .HasColumnName("is_leaf"); - - b.Property("IsSelectable") - .HasColumnType("boolean") - .HasColumnName("is_selectable"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MarkerConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("marker_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MarkerType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("marker_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("node_key"); - - b.Property("NodeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("node_type"); - - b.Property("ParentId") - .HasColumnType("uuid") - .HasColumnName("parent_id"); - - b.Property("Path") - .HasColumnType("ltree") - .HasColumnName("path"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_content_nodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_content_nodes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_content_nodes_created_by"); - - b.HasIndex("Path") - .HasDatabaseName("ix_content_nodes_path"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Path"), "gist"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_content_nodes_updated_by"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MarkerType") - .HasDatabaseName("ix_content_nodes_tenant_id_marker_type") - .HasFilter("marker_type is not null"); - - b.HasIndex("TenantId", "ParentId") - .HasDatabaseName("ix_content_nodes_tenant_id_parent_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_content_nodes_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "NodeKey") - .IsUnique() - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_node_key"); - - b.HasIndex("TenantId", "EntryId", "ParentId", "SortOrder") - .HasDatabaseName("ix_content_nodes_tenant_id_entry_id_parent_id_sort_order"); - - b.ToTable("content_nodes", null, t => - { - t.HasCheckConstraint("ck_content_nodes_depth", "depth >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_chapters"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_chapters_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_chapters_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_handbook_chapters_tenant_id_subject_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_chapters_tenant_id_entry_id_content_node_id_subjec~"); - - b.ToTable("handbook_chapters", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChapterId") - .HasColumnType("uuid") - .HasColumnName("chapter_id"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_entries"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_entries_tenant_id_id"); - - b.HasIndex("TenantId", "ChapterId") - .HasDatabaseName("ix_handbook_entries_tenant_id_chapter_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_entries_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_entries_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "ChapterId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_entries_tenant_id_entry_id_content_node_id_chapter~"); - - b.ToTable("handbook_entries", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Icon") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("MajorId") - .HasColumnType("uuid") - .HasColumnName("major_id"); - - b.Property("MajorLegacyIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("major_legacy_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_handbook_subjects"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_handbook_subjects_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_handbook_subjects_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "MajorId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_major_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId") - .HasDatabaseName("ix_handbook_subjects_tenant_id_school_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_handbook_subjects_tenant_id_entry_id_content_node_id_region~"); - - b.ToTable("handbook_subjects", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("CdnUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("cdn_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FileName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("file_name"); - - b.Property("IsPublic") - .HasColumnType("boolean") - .HasColumnName("is_public"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_images"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_images_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_images_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsPublic") - .HasDatabaseName("ix_images_tenant_id_category_is_public"); - - b.ToTable("images", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssemblyType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assembly_type"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PassScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("pass_score"); - - b.Property("QuestionLimit") - .HasColumnType("integer") - .HasColumnName("question_limit"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("Rules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Sections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sections") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_practice_blueprints"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_blueprints_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_practice_blueprints_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_practice_blueprints_updated_by"); - - b.HasIndex("TenantId", "CollectionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_collection_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_practice_blueprints_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_practice_blueprints_tenant_id_region_id"); - - b.HasIndex("TenantId", "NodeId", "CollectionId", "Mode", "Status", "SortOrder") - .HasDatabaseName("ix_practice_blueprints_tenant_id_node_id_collection_id_mode_st~"); - - b.ToTable("practice_blueprints", null, t => - { - t.HasCheckConstraint("ck_practice_blueprints_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_practice_blueprints_question_limit", "question_limit is null or question_limit > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("AllowedPlanCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("allowed_plan_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.PrimitiveCollection("AllowedRegionIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_region_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedSubjectIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_subject_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedTenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("GrantScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("grant_scope"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_bank_grants"); - - b.HasIndex("AllowedPlanCodes") - .HasDatabaseName("ix_question_bank_grants_allowed_plan_codes"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedPlanCodes"), "gin"); - - b.HasIndex("AllowedTenantIds") - .HasDatabaseName("ix_question_bank_grants_allowed_tenant_ids"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedTenantIds"), "gin"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_bank_grants_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_bank_grants_updated_by"); - - b.HasIndex("SourceQuestionBankId", "Status", "StartsAt", "ExpiresAt") - .HasDatabaseName("ix_question_bank_grants_source_question_bank_id_status_starts_~"); - - b.ToTable("question_bank_grants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessRules") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_rules") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("CollectionType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("collection_type"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Filters") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("filters") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_collections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collections_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_collections_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_collections_updated_by"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_question_collections_tenant_id_category_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_question_collections_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_collections_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_question_collections_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_collections_tenant_id_region_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_question_collections_tenant_id_subject_id"); - - b.HasIndex("TenantId", "NodeId", "Status", "SortOrder") - .HasDatabaseName("ix_question_collections_tenant_id_node_id_status_sort_order"); - - b.ToTable("question_collections", null, t => - { - t.HasCheckConstraint("ck_question_collections_duration", "duration_minutes is null or duration_minutes > 0"); - - t.HasCheckConstraint("ck_question_collections_question_count", "question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Required") - .HasColumnType("boolean") - .HasColumnName("required"); - - b.Property("Score") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_collection_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_collection_items_tenant_id_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_question_collection_items_tenant_id_question_id"); - - b.HasIndex("TenantId", "CollectionId", "QuestionId") - .IsUnique() - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_question_~"); - - b.HasIndex("TenantId", "CollectionId", "SectionKey", "SortOrder") - .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_section_k~"); - - b.ToTable("question_collection_items", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Types") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("types") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_type_groups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_type_groups_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_type_groups_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_question_type_groups_tenant_id_subject_id_is_active_sort_or~"); - - b.ToTable("question_type_groups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("LegacyVideoId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_video_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoId") - .HasColumnType("uuid") - .HasColumnName("video_id"); - - b.Property("VideoType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("video_type"); - - b.HasKey("Id") - .HasName("pk_question_videos"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_videos_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "VideoId") - .HasDatabaseName("ix_question_videos_tenant_id_video_id"); - - b.HasIndex("TenantId", "QuestionId", "VideoId", "VideoType") - .IsUnique() - .HasDatabaseName("ix_question_videos_tenant_id_question_id_video_id_video_type"); - - b.ToTable("question_videos", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("SourceSubjectId") - .HasColumnType("uuid") - .HasColumnName("source_subject_id"); - - b.Property("TargetSubjectId") - .HasColumnType("uuid") - .HasColumnName("target_subject_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacySourceSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_source_subject_id"); - - b.Property("LegacyTargetSubjectId") - .HasColumnType("text") - .HasColumnName("legacy_target_subject_id"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.HasKey("TenantId", "SourceSubjectId", "TargetSubjectId") - .HasName("pk_subject_shares"); - - b.HasIndex("TenantId", "TargetSubjectId") - .HasDatabaseName("ix_subject_shares_tenant_id_target_subject_id"); - - b.ToTable("subject_shares", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AdoptionMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("adoption_mode"); - - b.Property("CopiedQuestionCount") - .HasColumnType("integer") - .HasColumnName("copied_question_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("GrantId") - .HasColumnType("uuid") - .HasColumnName("grant_id"); - - b.Property("LastSyncedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_synced_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("SourceSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SyncStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("sync_status"); - - b.Property("TargetCollectionId") - .HasColumnType("uuid") - .HasColumnName("target_collection_id"); - - b.Property("TargetEntryId") - .HasColumnType("uuid") - .HasColumnName("target_entry_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_question_bank_adoptions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_question_bank_adoptions_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_created_by"); - - b.HasIndex("GrantId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_grant_id"); - - b.HasIndex("SourceQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_source_question_bank_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_updated_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .IsUnique() - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_source_question_ba~"); - - b.HasIndex("TenantId", "TargetCollectionId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_collection_~"); - - b.HasIndex("TenantId", "TargetEntryId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_entry_id"); - - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_question_ba~"); - - b.HasIndex("TenantId", "Status", "UpdatedAt") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_status_updated_at"); - - b.ToTable("tenant_question_bank_adoptions", null, t => - { - t.HasCheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FavoritedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("favorited_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Note") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.HasKey("Id") - .HasName("pk_user_word_favorites"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_favorites_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_favorites_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_favorites_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_favorites_tenant_id_user_id_word_id"); - - b.ToTable("user_word_favorites", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CorrectStreak") - .HasColumnType("integer") - .HasColumnName("correct_streak"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DueLevel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("due_level"); - - b.Property("EaseFactor") - .ValueGeneratedOnAdd() - .HasPrecision(4, 2) - .HasColumnType("numeric(4,2)") - .HasDefaultValue(2.50m) - .HasColumnName("ease_factor"); - - b.Property("LastResult") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("last_result"); - - b.Property("LastReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_review_at"); - - b.Property("LegacyId") - .HasColumnType("text") - .HasColumnName("legacy_id"); - - b.Property("LegacyUserId") - .HasColumnType("text") - .HasColumnName("legacy_user_id"); - - b.Property("LegacyWordId") - .HasColumnType("text") - .HasColumnName("legacy_word_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextReviewAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_review_at"); - - b.Property("ReviewCount") - .HasColumnType("integer") - .HasColumnName("review_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WordId") - .HasColumnType("uuid") - .HasColumnName("word_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_user_word_progress"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_word_progress_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_word_progress_user_id"); - - b.HasIndex("TenantId", "WordId") - .HasDatabaseName("ix_user_word_progress_tenant_id_word_id"); - - b.HasIndex("TenantId", "UserId", "WordId") - .IsUnique() - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_word_id"); - - b.HasIndex("TenantId", "UserId", "NextReviewAt", "Status", "DueLevel") - .HasDatabaseName("ix_user_word_progress_tenant_id_user_id_next_review_at_status_~"); - - b.ToTable("user_word_progress", null, t => - { - t.HasCheckConstraint("ck_user_word_progress_correct_streak", "correct_streak >= 0"); - - t.HasCheckConstraint("ck_user_word_progress_ease_factor", "ease_factor >= 1.30 and ease_factor <= 3.00"); - - t.HasCheckConstraint("ck_user_word_progress_review_count", "review_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("IsGeneral") - .HasColumnType("boolean") - .HasColumnName("is_general"); - - b.Property("KnowledgeTags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("knowledge_tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ThumbnailUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("thumbnail_url"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VideoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("video_url"); - - b.HasKey("Id") - .HasName("pk_video_explanations"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_video_explanations_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_video_explanations_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "SourceHash") - .HasDatabaseName("ix_video_explanations_tenant_id_source_hash") - .HasFilter("source_hash is not null"); - - b.HasIndex("TenantId", "SubjectId", "IsActive", "SortOrder") - .HasDatabaseName("ix_video_explanations_tenant_id_subject_id_is_active_sort_order"); - - b.ToTable("video_explanations", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WordCount") - .HasColumnType("integer") - .HasColumnName("word_count"); - - b.HasKey("Id") - .HasName("pk_vocabulary_units"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_units_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_units_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_vocabulary_units_tenant_id_region_id"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_units_tenant_id_entry_id_content_node_id_region_~"); - - b.ToTable("vocabulary_units", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("Example") - .HasColumnType("text") - .HasColumnName("example"); - - b.Property("ExampleTranslation") - .HasColumnType("text") - .HasColumnName("example_translation"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Meaning") - .HasColumnType("text") - .HasColumnName("meaning"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phonetic") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("phonetic"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitId") - .HasColumnType("uuid") - .HasColumnName("unit_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Word") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("word"); - - b.HasKey("Id") - .HasName("pk_vocabulary_words"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_vocabulary_words_tenant_id_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_vocabulary_words_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_vocabulary_words_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UnitId", "IsActive", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_vocabulary_words_tenant_id_unit_id_is_active_sort_order_cre~"); - - b.HasIndex("TenantId", "EntryId", "ContentNodeId", "UnitId", "IsActive", "SortOrder") - .HasDatabaseName("ix_vocabulary_words_tenant_id_entry_id_content_node_id_unit_id~"); - - b.ToTable("vocabulary_words", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("EffectiveRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("effective_rate"); - - b.Property("GeneratedBy") - .HasColumnType("uuid") - .HasColumnName("generated_by"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidBy") - .HasColumnType("uuid") - .HasColumnName("paid_by"); - - b.Property("PaidUserCount") - .HasColumnType("integer") - .HasColumnName("paid_user_count"); - - b.Property("PaymentAccount") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("payment_account"); - - b.Property("PaymentMethod") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_method"); - - b.Property("PeriodEnd") - .HasColumnType("date") - .HasColumnName("period_end"); - - b.Property("PeriodStart") - .HasColumnType("date") - .HasColumnName("period_start"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Remark") - .HasColumnType("text") - .HasColumnName("remark"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("SettlementNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("settlement_no"); - - b.Property("SourceCount") - .HasColumnType("integer") - .HasColumnName("source_count"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlements_tenant_id_id"); - - b.HasIndex("GeneratedBy") - .HasDatabaseName("ix_commission_settlements_generated_by"); - - b.HasIndex("PaidBy") - .HasDatabaseName("ix_commission_settlements_paid_by"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlements_referrer_user_id"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commission_settlements_reviewed_by"); - - b.HasIndex("TenantId", "SettlementNo") - .IsUnique() - .HasDatabaseName("ix_commission_settlements_tenant_id_settlement_no"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart") - .HasDatabaseName("ix_commission_settlements_referrer_start"); - - b.HasIndex("TenantId", "ReferrerUserId", "PeriodStart", "PeriodEnd") - .IsUnique() - .HasDatabaseName("ux_commission_settlements_referrer_period"); - - b.ToTable("commission_settlements", null, t => - { - t.HasCheckConstraint("ck_commission_settlements_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_counts", "source_count >= 0 and paid_user_count >= 0"); - - t.HasCheckConstraint("ck_commission_settlements_period", "period_end >= period_start"); - - t.HasCheckConstraint("ck_commission_settlements_rates", "default_rate >= 0 and default_rate <= 1 and (effective_rate is null or (effective_rate >= 0 and effective_rate <= 1))"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementExportEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ContentSha256") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("content_sha256"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExportFormat") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)") - .HasColumnName("export_format"); - - b.Property("ExportedBy") - .HasColumnType("uuid") - .HasColumnName("exported_by"); - - b.Property("Filename") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("filename"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RowCount") - .HasColumnType("integer") - .HasColumnName("row_count"); - - b.Property("SettlementId") - .HasColumnType("uuid") - .HasColumnName("settlement_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_commission_settlement_export_events"); - - b.HasIndex("ExportedBy") - .HasDatabaseName("ix_commission_settlement_export_events_exported_by"); - - b.HasIndex("TenantId", "SettlementId", "CreatedAt") - .HasDatabaseName("ix_commission_settlement_export_events_tenant_id_settlement_id~"); - - b.ToTable("commission_settlement_export_events", null, t => - { - t.HasCheckConstraint("ck_commission_export_events_rows", "row_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AttributionType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("attribution_type"); - - b.Property("CommissionAmountCents") - .HasColumnType("integer") - .HasColumnName("commission_amount_cents"); - - b.Property("CommissionRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("commission_rate"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrossAmountCents") - .HasColumnType("integer") - .HasColumnName("gross_amount_cents"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RateSource") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("rate_source"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("SettlementId") - .HasColumnType("uuid") - .HasColumnName("settlement_id"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceNo") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_no"); - - b.Property("SourcePaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("source_paid_at"); - - b.Property("SourceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlement_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlement_items_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_commission_settlement_items_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_commission_settlement_items_student_user_id"); - - b.HasIndex("TenantId", "SettlementId") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_settlement_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "SourcePaidAt") - .HasDatabaseName("ix_commission_settlement_items_tenant_id_referrer_user_id_sour~"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .IsUnique() - .HasDatabaseName("ix_commission_settlement_items_tenant_id_source_type_source_id"); - - b.ToTable("commission_settlement_items", null, t => - { - t.HasCheckConstraint("ck_commission_settlement_items_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0"); - - t.HasCheckConstraint("ck_commission_settlement_items_rate", "commission_rate >= 0 and commission_rate <= 1"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementProof", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("AssetId") - .HasColumnType("uuid") - .HasColumnName("asset_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("ExternalUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("external_url"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentAccount") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("payment_account"); - - b.Property("PaymentMethod") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_method"); - - b.Property("ProofType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("proof_type"); - - b.Property("ReviewNote") - .HasColumnType("text") - .HasColumnName("review_note"); - - b.Property("ReviewedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reviewed_at"); - - b.Property("ReviewedBy") - .HasColumnType("uuid") - .HasColumnName("reviewed_by"); - - b.Property("SettlementId") - .HasColumnType("uuid") - .HasColumnName("settlement_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubmittedBy") - .HasColumnType("uuid") - .HasColumnName("submitted_by"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_commission_settlement_proofs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_commission_settlement_proofs_tenant_id_id"); - - b.HasIndex("ReviewedBy") - .HasDatabaseName("ix_commission_settlement_proofs_reviewed_by"); - - b.HasIndex("SubmittedBy") - .HasDatabaseName("ix_commission_settlement_proofs_submitted_by"); - - b.HasIndex("TenantId", "AssetId") - .HasDatabaseName("ix_commission_settlement_proofs_tenant_id_asset_id"); - - b.HasIndex("TenantId", "SettlementId", "Status", "CreatedAt") - .HasDatabaseName("ix_commission_settlement_proofs_tenant_id_settlement_id_status~"); - - b.ToTable("commission_settlement_proofs", null, t => - { - t.HasCheckConstraint("ck_commission_settlement_proofs_amount", "amount_cents is null or amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignmentConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("AssignmentCursor") - .HasColumnType("integer") - .HasColumnName("assignment_cursor"); - - b.Property("AssignmentMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("AssignmentPool") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("assignment_pool") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DelaySeconds") - .HasColumnType("integer") - .HasColumnName("delay_seconds"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("ExamType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("exam_type"); - - b.Property("FormName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("form_name"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Url") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("url"); - - b.HasKey("Id") - .HasName("pk_crm_config"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_config_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_crm_config_tenant_id"); - - b.HasIndex("TenantId", "AssignmentMode") - .HasDatabaseName("ix_crm_config_tenant_id_assignment_mode"); - - b.ToTable("crm_config", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempt") - .HasColumnType("integer") - .HasColumnName("attempt"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("HttpCode") - .HasColumnType("integer") - .HasColumnName("http_code"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Outcome") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("outcome"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("RequestBody") - .HasColumnType("text") - .HasColumnName("request_body"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ResponseSummary") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("response_summary"); - - b.Property("SignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("signed_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_log"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_log_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_log_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_log_tenant_id_record_id_created_at"); - - b.ToTable("crm_webhook_log", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("LeadId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Payload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("RecordId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("record_id"); - - b.Property("ScheduledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Source") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TargetUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("target_url"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_crm_webhook_queue"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_crm_webhook_queue_tenant_id_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_idempotency_key") - .HasFilter("idempotency_key is not null"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RecordId") - .IsUnique() - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "RecordId", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_record_id_created_at") - .HasFilter("record_id is not null"); - - b.HasIndex("TenantId", "Source", "CreatedAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_source_created_at") - .HasFilter("source = 'tenant.student.crm_push'"); - - b.HasIndex("TenantId", "Status", "ScheduledAt", "NextAttemptAt") - .HasDatabaseName("ix_crm_webhook_queue_tenant_id_status_scheduled_at_next_attemp~"); - - b.ToTable("crm_webhook_queue", null, t => - { - t.HasCheckConstraint("ck_crm_webhook_queue_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Channel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LandingPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("landing_path"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_codes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_codes_user_id"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_code"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_referral_codes_tenant_id_user_id"); - - b.HasIndex("TenantId", "Code", "Status") - .HasDatabaseName("ix_referral_codes_tenant_id_code_status"); - - b.ToTable("referral_codes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("assigned_at"); - - b.Property("AssignedBy") - .HasColumnType("uuid") - .HasColumnName("assigned_by"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("AssignmentMode") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("assignment_mode"); - - b.Property("BindType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("bind_type"); - - b.Property("BoundAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("bound_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FirstTrackId") - .HasColumnType("uuid") - .HasColumnName("first_track_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ProtectedUntil") - .HasColumnType("timestamp with time zone") - .HasColumnName("protected_until"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_leads"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_leads_tenant_id_id"); - - b.HasIndex("AssignedBy") - .HasDatabaseName("ix_referral_leads_assigned_by"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_referral_leads_assigned_to_user_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_leads_referrer_user_id"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_referral_leads_student_user_id"); - - b.HasIndex("TenantId", "FirstTrackId") - .HasDatabaseName("ix_referral_leads_tenant_id_first_track_id"); - - b.HasIndex("TenantId", "StudentUserId") - .IsUnique() - .HasDatabaseName("ix_referral_leads_tenant_id_student_user_id"); - - b.HasIndex("TenantId", "ReferrerUserId", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_referrer_user_id_bound_at"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "BoundAt") - .HasDatabaseName("ix_referral_leads_tenant_id_assigned_to_user_id_status_bound_at"); - - b.ToTable("referral_leads", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ErrorMessage") - .HasColumnType("text") - .HasColumnName("error_message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Page") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("page"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("provider"); - - b.Property("QrcodeUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("qrcode_url"); - - b.Property("RefCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("Scene") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("scene"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_referral_qrcodes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_qrcodes_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_referral_qrcodes_user_id"); - - b.HasIndex("TenantId", "RefCode", "Status") - .HasDatabaseName("ix_referral_qrcodes_tenant_id_ref_code_status"); - - b.HasIndex("TenantId", "Provider", "Scene", "Page") - .IsUnique() - .HasDatabaseName("ix_referral_qrcodes_tenant_id_provider_scene_page"); - - b.ToTable("referral_qrcodes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LeaderUserId") - .HasColumnType("uuid") - .HasColumnName("leader_user_id"); - - b.Property("MemberUserId") - .HasColumnType("uuid") - .HasColumnName("member_user_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RelationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("relation_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_referral_team_edges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_team_edges_tenant_id_id"); - - b.HasIndex("LeaderUserId") - .HasDatabaseName("ix_referral_team_edges_leader_user_id"); - - b.HasIndex("MemberUserId") - .HasDatabaseName("ix_referral_team_edges_member_user_id"); - - b.HasIndex("TenantId", "LeaderUserId", "Status") - .HasDatabaseName("ix_referral_team_edges_tenant_id_leader_user_id_status"); - - b.HasIndex("TenantId", "MemberUserId", "RelationType") - .IsUnique() - .HasDatabaseName("ix_referral_team_edges_tenant_id_member_user_id_relation_type"); - - b.ToTable("referral_team_edges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("event_type"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("LeadId") - .HasColumnType("uuid") - .HasColumnName("lead_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RefCode") - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("ref_code"); - - b.Property("ReferrerUserId") - .HasColumnType("uuid") - .HasColumnName("referrer_user_id"); - - b.Property("Source") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source"); - - b.Property("TargetUserId") - .HasColumnType("uuid") - .HasColumnName("target_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_referral_tracks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_referral_tracks_tenant_id_id"); - - b.HasIndex("ReferrerUserId") - .HasDatabaseName("ix_referral_tracks_referrer_user_id"); - - b.HasIndex("TargetUserId") - .HasDatabaseName("ix_referral_tracks_target_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_referral_tracks_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "LeadId", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_lead_id_created_at"); - - b.HasIndex("TenantId", "RefCode", "EventType", "CreatedAt") - .HasDatabaseName("ix_referral_tracks_tenant_id_ref_code_event_type_created_at"); - - b.ToTable("referral_tracks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Config") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DefaultRate") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("default_rate"); - - b.Property("MinSettlementCents") - .HasColumnType("integer") - .HasColumnName("min_settlement_cents"); - - b.Property("SettlementCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("settlement_cycle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_commission_settings"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_commission_settings_tenant_id_id"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_commission_settings_tenant_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_commission_settings_updated_by"); - - b.ToTable("tenant_commission_settings", null, t => - { - t.HasCheckConstraint("ck_tenant_commission_settings_default_rate", "default_rate >= 0 and default_rate <= 1"); - - t.HasCheckConstraint("ck_tenant_commission_settings_min_settlement", "min_settlement_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AvatarPreset") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("male") - .HasColumnName("avatar_preset"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastCheckInDate") - .HasColumnType("date") - .HasColumnName("last_check_in_date"); - - b.Property("LegacyUserId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_user_id"); - - b.Property("MasteredWordsCount") - .HasColumnType("integer") - .HasColumnName("mastered_words_count"); - - b.Property("ModuleSelections") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_selections") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Progress") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("progress") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("QuestionsAnsweredToday") - .HasColumnType("integer") - .HasColumnName("questions_answered_today"); - - b.Property("RecentActivities") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("recent_activities") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SelectedMajorId") - .HasColumnType("uuid") - .HasColumnName("selected_major_id"); - - b.Property("SelectedSchoolId") - .HasColumnType("uuid") - .HasColumnName("selected_school_id"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_student_profiles"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_student_profiles_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_student_profiles_user_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_student_profiles_tenant_id_region_id"); - - b.HasIndex("TenantId", "SelectedMajorId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_major_id"); - - b.HasIndex("TenantId", "SelectedSchoolId") - .HasDatabaseName("ix_student_profiles_tenant_id_selected_school_id"); - - b.HasIndex("TenantId", "UserId") - .IsUnique() - .HasDatabaseName("ix_student_profiles_tenant_id_user_id"); - - b.ToTable("student_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AvatarUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("avatar_url"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("LastSeenAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyPasswordHash") - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasColumnName("legacy_password_hash"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PasswordMigrationRequired") - .HasColumnType("boolean") - .HasColumnName("password_migration_required"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("PrimaryRole") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("primary_role"); - - b.Property("RawProfile") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_profile") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Score") - .HasColumnType("integer") - .HasColumnName("score"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("Username") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Email") - .IsUnique() - .HasDatabaseName("ix_users_email"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_users_legacy_id"); - - b.HasIndex("Phone") - .IsUnique() - .HasDatabaseName("ix_users_phone"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Email") - .HasMaxLength(320) - .HasColumnType("citext") - .HasColumnName("email"); - - b.Property("OpenId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("open_id"); - - b.Property("Phone") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderSubject") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("provider_subject"); - - b.Property("SecretPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("secret_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UnionId") - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("union_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_identities"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_identities_user_id"); - - b.HasIndex("Provider", "ProviderSubject") - .IsUnique() - .HasDatabaseName("ix_user_identities_provider_provider_subject"); - - b.ToTable("user_identities", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CollectionName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FieldPath") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("field_path"); - - b.Property("IssueCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("issue_code"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("RawValueSample") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)") - .HasColumnName("raw_value_sample"); - - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_issues"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_issues_tenant_id_id"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_run_id"); - - b.HasIndex("RunId", "Severity", "CollectionName") - .HasDatabaseName("ix_pb_import_issues_run_id_severity_collection_name"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_import_issues_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_import_issues", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("SourceKind") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_kind"); - - b.Property("SourceName") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("source_name"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("Stats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("stats") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("Id") - .HasName("pk_pb_import_runs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_pb_import_runs_tenant_id_id"); - - b.HasIndex("TenantId", "Status", "StartedAt") - .HasDatabaseName("ix_pb_import_runs_tenant_id_status_started_at"); - - b.ToTable("pb_import_runs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.Property("RunId") - .HasColumnType("uuid") - .HasColumnName("run_id"); - - b.Property("CollectionName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("collection_name"); - - b.Property("LegacyId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("legacy_id"); - - b.Property("Errors") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("errors") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("ImportedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("imported_at") - .HasDefaultValueSql("now()"); - - b.Property("Normalized") - .HasColumnType("boolean") - .HasColumnName("normalized"); - - b.Property("Record") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("record") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.HasKey("RunId", "CollectionName", "LegacyId") - .HasName("pk_pb_raw_records"); - - b.HasIndex("TenantId", "RunId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_run_id"); - - b.HasIndex("TenantId", "CollectionName", "LegacyId") - .HasDatabaseName("ix_pb_raw_records_tenant_id_collection_name_legacy_id"); - - b.ToTable("pb_raw_records", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("AnsweredAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("answered_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsCorrect") - .HasColumnType("boolean") - .HasColumnName("is_correct"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyQuestionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_question_id"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("QuestionVersionId") - .HasColumnType("uuid") - .HasColumnName("question_version_id"); - - b.Property("SelectedOptions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("selected_options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_answer_records"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_answer_records_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_answer_records_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_answer_records_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id"); - - b.HasIndex("TenantId", "QuestionId", "QuestionVersionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id_question_version_id"); - - b.HasIndex("TenantId", "UserId", "PracticeSessionId") - .HasDatabaseName("ix_answer_records_tenant_id_user_id_practice_session_id"); - - b.ToTable("answer_records", null, t => - { - t.HasCheckConstraint("ck_answer_records_version_requires_question", "question_version_id is null or question_id is not null"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActiveUsers") - .HasColumnType("integer") - .HasColumnName("active_users"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("NewOrders") - .HasColumnType("integer") - .HasColumnName("new_orders"); - - b.Property("NewQuestions") - .HasColumnType("integer") - .HasColumnName("new_questions"); - - b.Property("NewRevenueCents") - .HasColumnType("integer") - .HasColumnName("new_revenue_cents"); - - b.Property("NewUsers") - .HasColumnType("integer") - .HasColumnName("new_users"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_dashboard_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_dashboard_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId") - .IsUnique() - .HasDatabaseName("ix_dashboard_daily_stats_tenant_id_stat_date_region_id"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId"), false); - - b.ToTable("dashboard_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("ExamAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("exam_at"); - - b.Property("ExamName") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("exam_name"); - - b.Property("ExamType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("exam_type"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SchoolId") - .HasColumnType("uuid") - .HasColumnName("school_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_exam_dates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_exam_dates_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_exam_dates_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_exam_dates_tenant_id_region_id"); - - b.HasIndex("TenantId", "SchoolId", "ExamAt") - .HasDatabaseName("ix_exam_dates_tenant_id_school_id_exam_at"); - - b.ToTable("exam_dates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Source") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("source"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_favorite_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_favorite_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_favorite_questions_tenant_id_question_id"); - - b.ToTable("favorite_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("access_mode"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EntitlementId") - .HasColumnType("uuid") - .HasColumnName("entitlement_id"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("GrantedCount") - .HasColumnType("integer") - .HasColumnName("granted_count"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("Reason") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("reason"); - - b.Property("RequestedCount") - .HasColumnType("integer") - .HasColumnName("requested_count"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_access_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_access_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_access_events_user_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_access_events_tenant_id_practice_session_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_practice_access_events_tenant_id_user_id_created_at"); - - b.ToTable("practice_access_events", null, t => - { - t.HasCheckConstraint("ck_practice_access_events_consumed_free_quota", "consumed_free_quota >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_granted_count", "granted_count >= 0"); - - t.HasCheckConstraint("ck_practice_access_events_requested_count", "requested_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FreeLimit") - .HasColumnType("integer") - .HasColumnName("free_limit"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScopeId") - .HasColumnType("uuid") - .HasColumnName("scope_id"); - - b.Property("ScopeType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("scope_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UsageDate") - .ValueGeneratedOnAdd() - .HasColumnType("date") - .HasColumnName("usage_date") - .HasDefaultValueSql("current_date"); - - b.Property("UsedCount") - .HasColumnType("integer") - .HasColumnName("used_count"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_daily_usage"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_daily_usage_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_practice_daily_usage_user_id"); - - b.HasIndex("TenantId", "UserId", "UsageDate") - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date"); - - b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId") - .IsUnique() - .HasDatabaseName("ix_practice_daily_usage_tenant_id_user_id_usage_date_scope_typ~"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "UserId", "UsageDate", "ScopeType", "ScopeId"), false); - - b.ToTable("practice_daily_usage", null, t => - { - t.HasCheckConstraint("ck_practice_daily_usage_free_limit", "free_limit >= 0"); - - t.HasCheckConstraint("ck_practice_daily_usage_used_count", "used_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AccessEntitlementId") - .HasColumnType("uuid") - .HasColumnName("access_entitlement_id"); - - b.Property("AccessMode") - .IsRequired() - .ValueGeneratedOnAdd() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasDefaultValue("free") - .HasColumnName("access_mode"); - - b.Property("AccessSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("access_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("ConsumedFreeQuota") - .HasColumnType("integer") - .HasColumnName("consumed_free_quota"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("DurationMinutes") - .HasColumnType("integer") - .HasColumnName("duration_minutes"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("FinishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at") - .HasDefaultValueSql("now()"); - - b.Property("TargetId") - .HasColumnType("uuid") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(8, 2) - .HasColumnType("numeric(8,2)") - .HasColumnName("total_score"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_practice_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_sessions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "UserId", "Id") - .HasName("ak_practice_sessions_tenant_id_user_id_id"); - - 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"); - - b.ToTable("practice_sessions", null, t => - { - t.HasCheckConstraint("ck_practice_sessions_consumed_free_quota", "consumed_free_quota >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("BlueprintId") - .HasColumnType("uuid") - .HasColumnName("blueprint_id"); - - b.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DurationSeconds") - .HasColumnType("integer") - .HasColumnName("duration_seconds"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("mode"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionResults") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_results") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionStats") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("section_stats") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_at"); - - b.Property("SubmittedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("submitted_at") - .HasDefaultValueSql("now()"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalQuestions") - .HasColumnType("integer") - .HasColumnName("total_questions"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.Property("WrongQuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("wrong_question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - - b.HasKey("Id") - .HasName("pk_practice_session_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_reports_tenant_id_id"); - - 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.ToTable("practice_session_reports", null, t => - { - t.HasCheckConstraint("ck_practice_session_reports_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_reports_counts", "total_questions >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_duration", "duration_seconds >= 0"); - - t.HasCheckConstraint("ck_practice_session_reports_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Accuracy") - .HasPrecision(6, 4) - .HasColumnType("numeric(6,4)") - .HasColumnName("accuracy"); - - b.Property("AnsweredCount") - .HasColumnType("integer") - .HasColumnName("answered_count"); - - b.Property("CorrectCount") - .HasColumnType("integer") - .HasColumnName("correct_count"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeSessionId") - .HasColumnType("uuid") - .HasColumnName("practice_session_id"); - - b.Property("QuestionCount") - .HasColumnType("integer") - .HasColumnName("question_count"); - - b.Property("QuestionType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("question_type"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("Score") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("score"); - - b.Property("SectionKey") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("section_key"); - - b.Property("SectionName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("section_name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalScore") - .HasPrecision(10, 2) - .HasColumnType("numeric(10,2)") - .HasColumnName("total_score"); - - b.Property("UnansweredCount") - .HasColumnType("integer") - .HasColumnName("unanswered_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WrongCount") - .HasColumnType("integer") - .HasColumnName("wrong_count"); - - b.HasKey("Id") - .HasName("pk_practice_session_report_sections"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_practice_session_report_sections_tenant_id_id"); - - b.HasIndex("TenantId", "PracticeSessionId") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_practice_session~"); - - b.HasIndex("TenantId", "ReportId", "SectionKey") - .IsUnique() - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sectio~"); - - b.HasIndex("TenantId", "ReportId", "SortOrder") - .HasDatabaseName("ix_practice_session_report_sections_tenant_id_report_id_sort_o~"); - - b.ToTable("practice_session_report_sections", null, t => - { - t.HasCheckConstraint("ck_practice_session_report_sections_accuracy", "accuracy >= 0 and accuracy <= 1"); - - t.HasCheckConstraint("ck_practice_session_report_sections_counts", "question_count >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0"); - - t.HasCheckConstraint("ck_practice_session_report_sections_scores", "score >= 0 and total_score >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Color") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("color"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LastAccessAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_access_at"); - - b.Property("LastPracticeAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_practice_at"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PracticeType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("practice_type"); - - b.Property("Progress") - .HasColumnType("integer") - .HasColumnName("progress"); - - b.Property("TargetLegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("target_legacy_id"); - - b.Property("TargetName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("target_name"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_recent_practices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_recent_practices_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_recent_practices_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_recent_practices_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "LastAccessAt") - .HasDatabaseName("ix_recent_practices_tenant_id_user_id_last_access_at"); - - b.ToTable("recent_practices", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attachments") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("attachments") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("Contact") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("contact"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("HandledAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("handled_at"); - - b.Property("HandledBy") - .HasColumnType("uuid") - .HasColumnName("handled_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("Resolution") - .HasColumnType("text") - .HasColumnName("resolution"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("Type") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_reports"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_reports_tenant_id_id"); - - b.HasIndex("HandledBy") - .HasDatabaseName("ix_reports_handled_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_reports_user_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_reports_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "QuestionId", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_question_id_created_at") - .HasFilter("question_id is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_reports_tenant_id_status_created_at"); - - b.ToTable("reports", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FromStatus") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("from_status"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("ReportId") - .HasColumnType("uuid") - .HasColumnName("report_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("ToStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("to_status"); - - b.HasKey("Id") - .HasName("pk_report_status_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_report_status_events_tenant_id_id"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_report_status_events_actor_user_id"); - - b.HasIndex("TenantId", "ReportId", "CreatedAt") - .HasDatabaseName("ix_report_status_events_tenant_id_report_id_created_at"); - - b.ToTable("report_status_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CodeCount") - .HasColumnType("integer") - .HasColumnName("code_count"); - - b.Property("CodeEstimated") - .HasColumnType("integer") - .HasColumnName("code_estimated"); - - b.Property("CodeUsed") - .HasColumnType("integer") - .HasColumnName("code_used"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EstimatedRevenueCents") - .HasColumnType("integer") - .HasColumnName("estimated_revenue_cents"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyRegionId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_region_id"); - - b.Property("OrderCount") - .HasColumnType("integer") - .HasColumnName("order_count"); - - b.Property("RealRevenueCents") - .HasColumnType("integer") - .HasColumnName("real_revenue_cents"); - - b.Property("RebuiltAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rebuilt_at"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SaleType") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("sale_type"); - - b.Property("StatDate") - .HasColumnType("date") - .HasColumnName("stat_date"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_revenue_daily_stats"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_revenue_daily_stats_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_region_id"); - - b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType") - .IsUnique() - .HasDatabaseName("ix_revenue_daily_stats_tenant_id_stat_date_region_id_sale_type"); - - NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("TenantId", "StatDate", "RegionId", "SaleType"), false); - - b.ToTable("revenue_daily_stats", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceAfter") - .HasColumnType("integer") - .HasColumnName("balance_after"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("event_type"); - - b.Property("IdempotencyKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("idempotency_key"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Points") - .HasColumnType("integer") - .HasColumnName("points"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_score_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_score_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_score_events_user_id"); - - b.HasIndex("TenantId", "IdempotencyKey") - .IsUnique() - .HasDatabaseName("ix_user_score_events_tenant_id_idempotency_key"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_user_score_events_tenant_id_user_id_created_at"); - - b.ToTable("user_score_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("LastWrongAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_wrong_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("WrongCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(1) - .HasColumnName("wrong_count"); - - b.HasKey("TenantId", "UserId", "QuestionId") - .HasName("pk_wrong_questions"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_wrong_questions_user_id"); - - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_wrong_questions_tenant_id_question_id"); - - b.ToTable("wrong_questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Link") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("link"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_announcements"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_announcements_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_announcements_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "IsActive", "SortOrder") - .HasDatabaseName("ix_announcements_tenant_id_is_active_sort_order"); - - b.ToTable("announcements", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("ActorUserId") - .HasColumnType("uuid") - .HasColumnName("actor_user_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IpAddress") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("ip_address"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_audit_logs"); - - b.HasIndex("ActorUserId") - .HasDatabaseName("ix_audit_logs_actor_user_id"); - - b.HasIndex("TenantId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_created_at"); - - b.HasIndex("TenantId", "TargetType", "TargetId", "CreatedAt") - .HasDatabaseName("ix_audit_logs_tenant_id_target_type_target_id_created_at"); - - b.ToTable("audit_logs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Category") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("category"); - - b.Property("ConditionExtra") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("condition_extra") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ConditionField") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("condition_field"); - - b.Property("ConditionOperator") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("condition_operator"); - - b.Property("ConditionValue") - .HasPrecision(18, 4) - .HasColumnType("numeric(18,4)") - .HasColumnName("condition_value"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("IconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("icon_url"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Level") - .HasColumnType("integer") - .HasColumnName("level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnlockType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("unlock_type"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_badges_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "Category", "IsActive", "SortOrder") - .HasDatabaseName("ix_badges_tenant_id_category_is_active_sort_order"); - - b.ToTable("badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BackgroundColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("background_color"); - - b.Property("BorderColor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("border_color"); - - b.Property("ButtonLink") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("button_link"); - - b.Property("ButtonText") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("button_text"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Subtitle") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("subtitle"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_banners"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_banners_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_banners_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_banners_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("banners", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Answer") - .HasColumnType("text") - .HasColumnName("answer"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("IsActive") - .HasColumnType("boolean") - .HasColumnName("is_active"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Question") - .HasColumnType("text") - .HasColumnName("question"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_faqs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_faqs_tenant_id_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_faqs_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId", "IsActive", "SortOrder") - .HasDatabaseName("ix_faqs_tenant_id_region_id_is_active_sort_order"); - - b.ToTable("faqs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("AdoptionId") - .HasColumnType("uuid") - .HasColumnName("adoption_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("ReadBy") - .HasColumnType("uuid") - .HasColumnName("read_by"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_content_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_content_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_content_notifications_created_by"); - - b.HasIndex("ReadBy") - .HasDatabaseName("ix_tenant_content_notifications_read_by"); - - b.HasIndex("TenantId", "SourceQuestionBankId") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_source_question_bank~"); - - b.HasIndex("TenantId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_notification_type_de~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_status_created_at"); - - b.HasIndex("TenantId", "AdoptionId", "NotificationType", "Status") - .HasDatabaseName("ix_tenant_content_notifications_tenant_id_adoption_id_notifica~"); - - b.ToTable("tenant_content_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActivePublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ActiveTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("active_template_code"); - - b.Property("ActiveTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("active_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DraftPublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftTemplateCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("draft_template_code"); - - b.Property("DraftTheme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("draft_theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("DraftUpdatedBy") - .HasColumnType("uuid") - .HasColumnName("draft_updated_by"); - - b.Property("PublishedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("published_at"); - - b.Property("PublishedBy") - .HasColumnType("uuid") - .HasColumnName("published_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_configs"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_theme_configs_tenant_id_id"); - - b.HasIndex("ActiveTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_active_template_code"); - - b.HasIndex("DraftTemplateCode") - .HasDatabaseName("ix_tenant_theme_configs_draft_template_code"); - - b.HasIndex("DraftUpdatedBy") - .HasDatabaseName("ix_tenant_theme_configs_draft_updated_by"); - - b.HasIndex("PublishedBy") - .HasDatabaseName("ix_tenant_theme_configs_published_by"); - - b.HasIndex("TenantId") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_configs_tenant_id"); - - b.ToTable("tenant_theme_configs", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("PreviewImageUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("preview_image_url"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_theme_templates"); - - b.HasAlternateKey("Code") - .HasName("ak_tenant_theme_templates_code"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_tenant_theme_templates_code"); - - b.HasIndex("Status", "SortOrder", "Code") - .HasDatabaseName("ix_tenant_theme_templates_status_sort_order_code"); - - b.ToTable("tenant_theme_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BadgeId") - .HasColumnType("uuid") - .HasColumnName("badge_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("GrantedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("granted_at"); - - b.Property("GrantedBy") - .HasColumnType("uuid") - .HasColumnName("granted_by"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_badges"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_badges_tenant_id_id"); - - b.HasIndex("GrantedBy") - .HasDatabaseName("ix_user_badges_granted_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_badges_user_id"); - - b.HasIndex("TenantId", "BadgeId") - .HasDatabaseName("ix_user_badges_tenant_id_badge_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "UserId", "BadgeId") - .IsUnique() - .HasDatabaseName("ix_user_badges_tenant_id_user_id_badge_id"); - - b.ToTable("user_badges", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ActionLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("action_label"); - - b.Property("ActionPath") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("action_path"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DedupeKey") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("dedupe_key"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("notification_type"); - - b.Property("ReadAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("read_at"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("SourceId") - .HasColumnType("uuid") - .HasColumnName("source_id"); - - b.Property("SourceType") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("source_type"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_user_notifications"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_user_notifications_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_user_notifications_created_by"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_user_notifications_user_id"); - - b.HasIndex("TenantId", "SourceType", "SourceId") - .HasDatabaseName("ix_user_notifications_tenant_id_source_type_source_id"); - - b.HasIndex("TenantId", "UserId", "NotificationType", "DedupeKey") - .IsUnique() - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_notification_type_dedu~") - .HasFilter("dedupe_key is not null"); - - b.HasIndex("TenantId", "UserId", "Status", "CreatedAt") - .HasDatabaseName("ix_user_notifications_tenant_id_user_id_status_created_at"); - - b.ToTable("user_notifications", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("AcknowledgedBy") - .HasColumnType("uuid") - .HasColumnName("acknowledged_by"); - - b.Property("Action") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("action"); - - b.Property("AuditLogId") - .HasColumnType("uuid") - .HasColumnName("audit_log_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Details") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("details") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("FirstSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("first_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("LastSeenAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("last_seen_at") - .HasDefaultValueSql("now()"); - - b.Property("ResolutionNote") - .HasColumnType("text") - .HasColumnName("resolution_note"); - - b.Property("ResolvedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("resolved_at"); - - b.Property("ResolvedBy") - .HasColumnType("uuid") - .HasColumnName("resolved_by"); - - b.Property("RuleId") - .HasColumnType("uuid") - .HasColumnName("rule_id"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("Summary") - .HasColumnType("text") - .HasColumnName("summary"); - - b.Property("TargetId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_id"); - - b.Property("TargetType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("target_type"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alerts"); - - b.HasIndex("AcknowledgedBy") - .HasDatabaseName("ix_platform_audit_alerts_acknowledged_by"); - - b.HasIndex("AuditLogId") - .HasDatabaseName("ix_platform_audit_alerts_audit_log_id"); - - b.HasIndex("ResolvedBy") - .HasDatabaseName("ix_platform_audit_alerts_resolved_by"); - - b.HasIndex("RuleId", "AuditLogId") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alerts_rule_id_audit_log_id"); - - b.HasIndex("Status", "Severity", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_status_severity_created_at"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_audit_alerts_tenant_id_status_created_at"); - - b.ToTable("platform_audit_alerts", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("ActionPatterns") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("action_patterns") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("Conditions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("conditions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Severity") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("severity"); - - b.PrimitiveCollection("TargetTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("target_types") - .HasDefaultValueSql("'{}'::text[]"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_audit_alert_rules"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_audit_alert_rules_code"); - - b.HasIndex("TenantId") - .HasDatabaseName("ix_platform_audit_alert_rules_tenant_id"); - - b.HasIndex("Enabled", "Severity", "Code") - .HasDatabaseName("ix_platform_audit_alert_rules_enabled_severity_code"); - - b.ToTable("platform_audit_alert_rules", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationChannel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ChannelCode") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("channel_code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("Enabled") - .HasColumnType("boolean") - .HasColumnName("enabled"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("MinReminderLevel") - .HasColumnType("integer") - .HasColumnName("min_reminder_level"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.PrimitiveCollection("ReminderChannels") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_channels") - .HasDefaultValueSql("array['internal']::text[]"); - - b.PrimitiveCollection("ReminderTypes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("reminder_types") - .HasDefaultValueSql("array['overdue', 'final_notice']::text[]"); - - b.Property("SecretRef") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.PrimitiveCollection("TenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("TimeoutSeconds") - .HasColumnType("integer") - .HasColumnName("timeout_seconds"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("WebhookUrl") - .IsRequired() - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("webhook_url"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_channels"); - - b.HasIndex("ChannelCode") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_channels_channel_code"); - - b.HasIndex("Enabled", "MinReminderLevel", "ChannelCode") - .HasDatabaseName("ix_platform_dunning_notification_channels_enabled_min_reminder~"); - - b.ToTable("platform_dunning_notification_channels", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_channels_level", "min_reminder_level between 1 and 20"); - - t.HasCheckConstraint("ck_platform_dunning_channels_timeout", "timeout_seconds between 1 and 60"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("ChannelId") - .HasColumnType("uuid") - .HasColumnName("channel_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("LastAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempt_at"); - - b.Property("LastError") - .HasColumnType("text") - .HasColumnName("last_error"); - - b.Property("LastHttpCode") - .HasColumnType("integer") - .HasColumnName("last_http_code"); - - b.Property("LastResponseSummary") - .HasColumnType("text") - .HasColumnName("last_response_summary"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NextAttemptAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("next_attempt_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("provider"); - - b.Property("ReminderId") - .HasColumnType("uuid") - .HasColumnName("reminder_id"); - - b.Property("RequestPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("request_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ScheduledAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("scheduled_at") - .HasDefaultValueSql("now()"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_dunning_notification_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_platform_dunning_notification_events_tenant_id_id"); - - b.HasIndex("ChannelId", "ReminderId") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_events_channel_id_reminder_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_invoice_id"); - - b.HasIndex("TenantId", "ReminderId") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_reminder_id"); - - b.HasIndex("InvoiceId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_invoice_id_status_crea~"); - - b.HasIndex("ReminderId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_reminder_id_status_cre~"); - - b.HasIndex("TenantId", "Status", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_status_creat~"); - - b.HasIndex("Status", "NextAttemptAt", "ScheduledAt", "CreatedAt") - .HasDatabaseName("ix_platform_dunning_notification_events_status_next_attempt_at~"); - - b.ToTable("platform_dunning_notification_events", null, t => - { - t.HasCheckConstraint("ck_platform_dunning_events_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformSaasPlan", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseAmountCents") - .HasColumnType("integer") - .HasColumnName("base_amount_cents"); - - b.Property("BillingCycle") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_cycle"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IncludedQuotas") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("included_quotas") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OveragePrices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("overage_prices") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_platform_saas_plans"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_platform_saas_plans_code"); - - b.HasIndex("Status", "SortOrder") - .HasDatabaseName("ix_platform_saas_plans_status_sort_order"); - - b.ToTable("platform_saas_plans", null, t => - { - t.HasCheckConstraint("ck_platform_saas_plans_amount", "base_amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BankAccountMasked") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("bank_account_masked"); - - b.Property("BankName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("bank_name"); - - b.Property("BillingAddress") - .HasColumnType("text") - .HasColumnName("billing_address"); - - b.Property("BillingName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("billing_name"); - - b.Property("ContactEmail") - .HasMaxLength(300) - .HasColumnType("citext") - .HasColumnName("contact_email"); - - b.Property("ContactName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("contact_name"); - - b.Property("ContactPhone") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("contact_phone"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceTitle") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("invoice_title"); - - b.Property("InvoiceType") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("TaxId") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("tax_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_billing_profiles"); - - b.ToTable("tenant_billing_profiles", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BalanceCents") - .HasColumnType("integer") - .HasColumnName("balance_cents"); - - b.Property("BillingPeriodEnd") - .HasColumnType("date") - .HasColumnName("billing_period_end"); - - b.Property("BillingPeriodStart") - .HasColumnType("date") - .HasColumnName("billing_period_start"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Currency") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("currency"); - - b.Property("DiscountCents") - .HasColumnType("integer") - .HasColumnName("discount_cents"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("invoice_no"); - - b.Property("InvoiceType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("invoice_type"); - - b.Property("IssuedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("issued_at"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Note") - .HasColumnType("text") - .HasColumnName("note"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaidCents") - .HasColumnType("integer") - .HasColumnName("paid_cents"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubtotalCents") - .HasColumnType("integer") - .HasColumnName("subtotal_cents"); - - b.Property("TaxCents") - .HasColumnType("integer") - .HasColumnName("tax_cents"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TotalCents") - .HasColumnType("integer") - .HasColumnName("total_cents"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoices"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoices_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoices_created_by"); - - b.HasIndex("InvoiceNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_invoice_no"); - - b.HasIndex("TenantId", "BillingPeriodStart", "BillingPeriodEnd") - .IsUnique() - .HasDatabaseName("ix_tenant_invoices_tenant_id_billing_period_start_billing_peri~") - .HasFilter("invoice_type = 'usage_overage' and status <> 'void' and metadata->>'source' = 'usage_overage_auto'"); - - b.HasIndex("TenantId", "Status", "DueDate") - .HasDatabaseName("ix_tenant_invoices_tenant_id_status_due_date"); - - b.ToTable("tenant_invoices", null, t => - { - t.HasCheckConstraint("ck_tenant_invoices_amounts", "subtotal_cents >= 0 and discount_cents >= 0 and tax_cents >= 0 and total_cents >= 0 and paid_cents >= 0 and balance_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoices_period", "billing_period_end is null or billing_period_start is null or billing_period_end >= billing_period_start"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("description"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("ItemRefId") - .HasColumnType("uuid") - .HasColumnName("item_ref_id"); - - b.Property("ItemType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("item_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Quantity") - .HasPrecision(12, 2) - .HasColumnType("numeric(12,2)") - .HasColumnName("quantity"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UnitAmountCents") - .HasColumnType("integer") - .HasColumnName("unit_amount_cents"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_items"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_items_tenant_id_id"); - - b.HasIndex("InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_invoice_id"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_items_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_items", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_items_amounts", "unit_amount_cents >= 0 and amount_cents >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_items_quantity", "quantity > 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AmountCents") - .HasColumnType("integer") - .HasColumnName("amount_cents"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Method") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("method"); - - b.Property("PaidAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("paid_at"); - - b.Property("PaymentNo") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("payment_no"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("ProviderTradeNo") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("provider_trade_no"); - - b.Property("RawPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("raw_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReceivedBy") - .HasColumnType("uuid") - .HasColumnName("received_by"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_payments"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_payments_tenant_id_id"); - - b.HasIndex("PaymentNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_payments_payment_no"); - - b.HasIndex("ReceivedBy") - .HasDatabaseName("ix_tenant_invoice_payments_received_by"); - - b.HasIndex("InvoiceId", "Status") - .HasDatabaseName("ix_tenant_invoice_payments_invoice_id_status"); - - b.HasIndex("TenantId", "InvoiceId") - .HasDatabaseName("ix_tenant_invoice_payments_tenant_id_invoice_id"); - - b.ToTable("tenant_invoice_payments", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AcknowledgedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("acknowledged_at"); - - b.Property("BalanceCentsSnapshot") - .HasColumnType("integer") - .HasColumnName("balance_cents_snapshot"); - - b.Property("Channel") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("channel"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DueDate") - .HasColumnType("date") - .HasColumnName("due_date"); - - b.Property("InvoiceId") - .HasColumnType("uuid") - .HasColumnName("invoice_id"); - - b.Property("Message") - .HasColumnType("text") - .HasColumnName("message"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ReminderDate") - .HasColumnType("date") - .HasColumnName("reminder_date"); - - b.Property("ReminderLevel") - .HasColumnType("integer") - .HasColumnName("reminder_level"); - - b.Property("ReminderType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("reminder_type"); - - b.Property("SentAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("sent_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_invoice_reminders"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_invoice_reminders_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_invoice_reminders_created_by"); - - b.HasIndex("InvoiceId", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_invoice_id_reminder_date"); - - b.HasIndex("TenantId", "Status", "ReminderDate") - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_status_reminder_date"); - - b.HasIndex("TenantId", "InvoiceId", "ReminderType", "Channel", "ReminderDate") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_reminders_tenant_id_invoice_id_reminder_type~"); - - b.ToTable("tenant_invoice_reminders", null, t => - { - t.HasCheckConstraint("ck_tenant_invoice_reminders_balance", "balance_cents_snapshot >= 0"); - - t.HasCheckConstraint("ck_tenant_invoice_reminders_level", "reminder_level between 1 and 20"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CategoryId") - .HasColumnType("uuid") - .HasColumnName("category_id"); - - b.Property("ContentNodeId") - .HasColumnType("uuid") - .HasColumnName("content_node_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CurrentVersionId") - .HasColumnType("uuid") - .HasColumnName("current_version_id"); - - b.Property("Difficulty") - .HasColumnType("integer") - .HasColumnName("difficulty"); - - b.Property("EntryId") - .HasColumnType("uuid") - .HasColumnName("entry_id"); - - b.Property("ExamMarkers") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("exam_markers") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("HasVideoExplanation") - .HasColumnType("boolean") - .HasColumnName("has_video_explanation"); - - b.Property("LegacyCategoryId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_category_id"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegacyNodeId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_node_id"); - - b.Property("LegacySubjectId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_subject_id"); - - b.Property("MediaUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("media_url"); - - b.Property("NodeId") - .HasColumnType("uuid") - .HasColumnName("node_id"); - - b.Property("PrimaryCollectionId") - .HasColumnType("uuid") - .HasColumnName("primary_collection_id"); - - b.Property("QuestionBankId") - .HasColumnType("uuid") - .HasColumnName("question_bank_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SubjectId") - .HasColumnType("uuid") - .HasColumnName("subject_id"); - - b.Property("Tags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("tags") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("type"); - - b.Property("TypeLabel") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("type_label"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_questions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_questions_tenant_id_id"); - - b.HasIndex("TenantId", "CategoryId") - .HasDatabaseName("ix_questions_tenant_id_category_id"); - - b.HasIndex("TenantId", "ContentNodeId") - .HasDatabaseName("ix_questions_tenant_id_content_node_id"); - - b.HasIndex("TenantId", "EntryId") - .HasDatabaseName("ix_questions_tenant_id_entry_id"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_questions_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "NodeId") - .HasDatabaseName("ix_questions_tenant_id_node_id"); - - b.HasIndex("TenantId", "PrimaryCollectionId") - .HasDatabaseName("ix_questions_tenant_id_primary_collection_id"); - - b.HasIndex("TenantId", "QuestionBankId") - .HasDatabaseName("ix_questions_tenant_id_question_bank_id"); - - b.HasIndex("TenantId", "SubjectId") - .HasDatabaseName("ix_questions_tenant_id_subject_id"); - - b.HasIndex("TenantId", "Id", "CurrentVersionId") - .HasDatabaseName("ix_questions_tenant_id_id_current_version_id"); - - b.ToTable("questions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SourceScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_scope"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_question_banks"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_banks_tenant_id_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_question_banks_tenant_id_region_id"); - - b.ToTable("question_banks", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AnswerText") - .HasColumnType("text") - .HasColumnName("answer_text"); - - b.Property("CodeLang") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("code_lang"); - - b.Property("CodeTemplate") - .HasColumnType("text") - .HasColumnName("code_template"); - - b.Property("Content") - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CorrectOptionIndex") - .HasColumnType("integer") - .HasColumnName("correct_option_index"); - - b.Property("CorrectOptionIndices") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("correct_option_indices") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Explanation") - .HasColumnType("text") - .HasColumnName("explanation"); - - b.Property("Options") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("options") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("SourceHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("source_hash"); - - b.Property("SubQuestions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("sub_questions") - .HasDefaultValueSql("'[]'::jsonb"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("VersionNo") - .HasColumnType("integer") - .HasColumnName("version_no"); - - b.HasKey("Id") - .HasName("pk_question_versions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_question_versions_tenant_id_id"); - - b.HasAlternateKey("TenantId", "QuestionId", "Id") - .HasName("ak_question_versions_tenant_id_question_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_versions_created_by"); - - b.HasIndex("QuestionId", "VersionNo") - .IsUnique() - .HasDatabaseName("ix_question_versions_question_id_version_no"); - - b.ToTable("question_versions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FailureCode") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("failure_code"); - - b.Property("Identifier") - .HasMaxLength(320) - .HasColumnType("character varying(320)") - .HasColumnName("identifier"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Result") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("result"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_login_events"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_login_events_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_login_events_user_id"); - - b.HasIndex("TenantId", "UserId", "CreatedAt") - .HasDatabaseName("ix_auth_login_events_tenant_id_user_id_created_at"); - - b.ToTable("auth_login_events", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("revoked_at"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("TokenHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("token_hash"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_auth_sessions"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_auth_sessions_tenant_id_id"); - - b.HasIndex("TokenHash") - .IsUnique() - .HasDatabaseName("ix_auth_sessions_token_hash"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_auth_sessions_user_id"); - - b.HasIndex("TenantId", "UserId", "ExpiresAt") - .HasDatabaseName("ix_auth_sessions_tenant_id_user_id_expires_at") - .HasFilter("revoked_at is null"); - - b.ToTable("auth_sessions", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Dimension") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("dimension"); - - b.Property("ScopeHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)") - .HasColumnName("scope_hash"); - - b.Property("BucketStart") - .HasColumnType("timestamp with time zone") - .HasColumnName("bucket_start"); - - b.Property("RequestCount") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(0) - .HasColumnName("request_count"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId", "Dimension", "ScopeHash", "BucketStart") - .HasName("pk_sms_send_rate_limits"); - - b.HasIndex("UpdatedAt") - .HasDatabaseName("ix_sms_send_rate_limits_updated_at"); - - b.ToTable("sms_send_rate_limits", null, t => - { - t.HasCheckConstraint("ck_sms_send_rate_limits_request_count", "request_count >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Attempts") - .HasColumnType("integer") - .HasColumnName("attempts"); - - b.Property("CodeHash") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("code_hash"); - - b.Property("ConsumedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("consumed_at"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("IpAddress") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("ip_address"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Phone") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("phone"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Purpose") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("purpose"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UserAgent") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)") - .HasColumnName("user_agent"); - - b.HasKey("Id") - .HasName("pk_sms_verification_codes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_sms_verification_codes_tenant_id_id"); - - b.HasIndex("TenantId", "Phone", "Purpose") - .IsUnique() - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "CreatedAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_created_at") - .HasFilter("consumed_at is null and status in ('pending', 'sent')"); - - b.HasIndex("TenantId", "Phone", "Purpose", "ExpiresAt") - .HasDatabaseName("ix_sms_verification_codes_tenant_id_phone_purpose_expires_at"); - - b.ToTable("sms_verification_codes", null, t => - { - t.HasCheckConstraint("ck_sms_verification_codes_attempts", "attempts >= 0"); - }); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BillingStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("billing_status"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("LegalName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("legal_name"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Mode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("mode"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("OwnerUserId") - .HasColumnType("uuid") - .HasColumnName("owner_user_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("citext") - .HasColumnName("slug"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenants"); - - b.HasIndex("LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenants_legacy_id"); - - b.HasIndex("OwnerUserId") - .HasDatabaseName("ix_tenants_owner_user_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_tenants_slug"); - - b.ToTable("tenants", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ConfigPublic") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("config_public") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_auth_providers"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_auth_providers_tenant_id_id"); - - b.HasIndex("TenantId", "Provider") - .IsUnique() - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider"); - - b.HasIndex("TenantId", "Provider", "Status") - .HasDatabaseName("ix_tenant_auth_providers_tenant_id_provider_status"); - - b.ToTable("tenant_auth_providers", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("BrandName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("brand_name"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FaviconUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("favicon_url"); - - b.Property("LogoUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)") - .HasColumnName("logo_url"); - - b.Property("OrganizationName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("organization_name"); - - b.Property("PublicAssets") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_assets") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ServiceAccountName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("service_account_name"); - - b.Property("ServiceWechat") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("service_wechat"); - - b.Property("ShortName") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("short_name"); - - b.Property("Slogan") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("slogan"); - - b.Property("Theme") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("theme") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_branding"); - - b.ToTable("tenant_branding", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Code") - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("LegacyId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("legacy_id"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("RegionId") - .HasColumnType("uuid") - .HasColumnName("region_id"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_classes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_classes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_classes_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_classes_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_code") - .HasFilter("code is not null and code <> ''"); - - b.HasIndex("TenantId", "LegacyId") - .IsUnique() - .HasDatabaseName("ix_tenant_classes_tenant_id_legacy_id"); - - b.HasIndex("TenantId", "RegionId") - .HasDatabaseName("ix_tenant_classes_tenant_id_region_id") - .HasFilter("region_id is not null"); - - b.HasIndex("TenantId", "Status", "SortOrder", "CreatedAt") - .HasDatabaseName("ix_tenant_classes_tenant_id_status_sort_order_created_at"); - - b.ToTable("tenant_classes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("JoinedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("joined_at") - .HasDefaultValueSql("now()"); - - b.Property("LeftAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("left_at"); - - b.Property("MemberType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("member_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_class_members"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_class_members_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_class_members_user_id"); - - b.HasIndex("TenantId", "ClassId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_status_member_type"); - - b.HasIndex("TenantId", "ClassId", "UserId", "MemberType") - .IsUnique() - .HasDatabaseName("ix_tenant_class_members_tenant_id_class_id_user_id_member_type"); - - b.HasIndex("TenantId", "UserId", "Status", "MemberType") - .HasDatabaseName("ix_tenant_class_members_tenant_id_user_id_status_member_type"); - - b.ToTable("tenant_class_members", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("DomainType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("domain_type"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(253) - .HasColumnType("citext") - .HasColumnName("host"); - - b.Property("IsPrimary") - .HasColumnType("boolean") - .HasColumnName("is_primary"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("VerificationToken") - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("verification_token"); - - b.Property("VerifiedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("verified_at"); - - b.HasKey("Id") - .HasName("pk_tenant_domains"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_domains_tenant_id_id"); - - b.HasIndex("Host") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_host"); - - b.HasIndex("TenantId", "IsPrimary") - .IsUnique() - .HasDatabaseName("ix_tenant_domains_tenant_id_is_primary") - .HasFilter("is_primary"); - - b.ToTable("tenant_domains", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("LegacyRole") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("legacy_role"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Role") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("role"); - - b.Property("RoleTemplateId") - .HasColumnType("uuid") - .HasColumnName("role_template_id"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.HasKey("Id") - .HasName("pk_tenant_memberships"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_memberships_tenant_id_id"); - - b.HasIndex("UserId") - .HasDatabaseName("ix_tenant_memberships_user_id"); - - b.HasIndex("TenantId", "RoleTemplateId") - .HasDatabaseName("ix_tenant_memberships_tenant_id_role_template_id"); - - b.HasIndex("TenantId", "UserId", "Role") - .IsUnique() - .HasDatabaseName("ix_tenant_memberships_tenant_id_user_id_role"); - - b.ToTable("tenant_memberships", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("BaseRole") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("base_role"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("code"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("DataScope") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("data_scope") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("FieldPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("field_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("IsSystem") - .HasColumnType("boolean") - .HasColumnName("is_system"); - - b.Property("MenuPermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("menu_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("ModulePermissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("module_permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("Permissions") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("permissions") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SortOrder") - .HasColumnType("integer") - .HasColumnName("sort_order"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_role_templates"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_role_templates_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_role_templates_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_role_templates_updated_by"); - - b.HasIndex("TenantId", "Code") - .IsUnique() - .HasDatabaseName("ix_tenant_role_templates_tenant_id_code"); - - b.HasIndex("TenantId", "Status", "SortOrder") - .HasDatabaseName("ix_tenant_role_templates_tenant_id_status_sort_order"); - - b.ToTable("tenant_role_templates", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("provider"); - - b.Property("Purpose") - .IsRequired() - .HasMaxLength(80) - .HasColumnType("character varying(80)") - .HasColumnName("purpose"); - - b.Property("RotatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("rotated_at"); - - b.Property("SecretKey") - .IsRequired() - .HasMaxLength(120) - .HasColumnType("character varying(120)") - .HasColumnName("secret_key"); - - b.Property("SecretPayload") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("secret_payload") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SecretRef") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("secret_ref"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("Id") - .HasName("pk_tenant_secrets"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_secrets_tenant_id_id"); - - b.HasIndex("TenantId", "SecretRef") - .IsUnique() - .HasDatabaseName("ix_tenant_secrets_tenant_id_secret_ref"); - - b.HasIndex("TenantId", "Purpose", "Provider", "SecretKey") - .IsUnique() - .HasDatabaseName("ix_tenant_secrets_tenant_id_purpose_provider_secret_key"); - - b.HasIndex("TenantId", "Purpose", "Provider", "Status") - .HasDatabaseName("ix_tenant_secrets_tenant_id_purpose_provider_status"); - - b.ToTable("tenant_secrets", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("AdminFeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("admin_feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("FeatureFlags") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("feature_flags") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("PublicConfig") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("public_config") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.HasKey("TenantId") - .HasName("pk_tenant_settings"); - - b.ToTable("tenant_settings", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AssignedToUserId") - .HasColumnType("uuid") - .HasColumnName("assigned_to_user_id"); - - b.Property("ClassId") - .HasColumnType("uuid") - .HasColumnName("class_id"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("completed_at"); - - b.Property("CompletedBy") - .HasColumnType("uuid") - .HasColumnName("completed_by"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("DueAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("due_at"); - - b.Property("FollowupType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("followup_type"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("priority"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("title"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_tenant_student_followups"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_followups_tenant_id_id"); - - b.HasIndex("AssignedToUserId") - .HasDatabaseName("ix_tenant_student_followups_assigned_to_user_id"); - - b.HasIndex("CompletedBy") - .HasDatabaseName("ix_tenant_student_followups_completed_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_followups_student_user_id"); - - b.HasIndex("TenantId", "AssignedToUserId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_assigned_to_user_id_stat~") - .HasFilter("assigned_to_user_id is not null"); - - b.HasIndex("TenantId", "ClassId", "Status", "DueAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_class_id_status_due_at") - .HasFilter("class_id is not null"); - - b.HasIndex("TenantId", "StudentUserId", "Status", "DueAt", "CreatedAt") - .HasDatabaseName("ix_tenant_student_followups_tenant_id_student_user_id_status_d~"); - - b.ToTable("tenant_student_followups", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text") - .HasColumnName("content"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("IsPinned") - .HasColumnType("boolean") - .HasColumnName("is_pinned"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("NoteType") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("note_type"); - - b.Property("StudentUserId") - .HasColumnType("uuid") - .HasColumnName("student_user_id"); - - b.Property("TenantId") - .HasColumnType("uuid") - .HasColumnName("tenant_id"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.Property("Visibility") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("visibility"); - - b.HasKey("Id") - .HasName("pk_tenant_student_notes"); - - b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_student_notes_tenant_id_id"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_student_notes_created_by"); - - b.HasIndex("StudentUserId") - .HasDatabaseName("ix_tenant_student_notes_student_user_id"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_student_notes_updated_by"); - - b.HasIndex("TenantId", "CreatedBy", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_created_by_created_at") - .HasFilter("created_by is not null"); - - b.HasIndex("TenantId", "StudentUserId", "IsPinned", "CreatedAt") - .HasDatabaseName("ix_tenant_student_notes_tenant_id_student_user_id_is_pinned_cr~"); - - b.ToTable("tenant_student_notes", (string)null); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Category", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_categories_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_categories_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Major", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_majors_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_majors_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ModuleNode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_module_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_module_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_module_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_regions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.RegionModule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_region_modules_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_region_modules_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.School", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_schools_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_schools_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineField", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_scoreline_fields_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_fields_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.ScorelineRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_scoreline_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_scoreline_records_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Catalog.Subject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.RegionModule", null) - .WithMany() - .HasForeignKey("TenantId", "ModuleId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_region_modules_tenant_id_module_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AgentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_agent_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_activation_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UsedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_users_used_by"); - - b.HasOne("Tiku.Domain.Commerce.CodeBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_code_batches_tenant_id_batch_id"); - - b.HasOne("Tiku.Domain.Commerce.CouponRedemption", null) - .WithMany() - .HasForeignKey("TenantId", "CouponRedemptionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_coupon_redemptions_tenant_id_coupon_redemp~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "UsedRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_activation_codes_regions_tenant_id_used_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CodeBatch", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_code_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationBatch", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_batches_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_batches_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssue", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedTo") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_assigned_to"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issues_users_resolved_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_batc~"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_reconciliation_item~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_issues_commerce_refund_requests_ten~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationIssueEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_reconciliation_issue_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationIssue", null) - .WithMany() - .HasForeignKey("TenantId", "IssueId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_issue_events_commerce_reconciliatio~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceReconciliationItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceReconciliationBatch", null) - .WithMany() - .HasForeignKey("TenantId", "BatchId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_reconciliation_items_commerce_reconciliation_batch~"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_payments_tenant_id_payment_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_reconciliation_items_commerce_refund_requests_tena~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.CommerceRefundRequest", null) - .WithMany() - .HasForeignKey("TenantId", "RefundRequestId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_events_commerce_refund_requests_tenant_id_r~"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CommerceRefundRequest", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ProcessedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_processed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RequestedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_requested_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commerce_refund_requests_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commerce_refund_requests_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_commerce_refund_requests_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Coupon", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupons_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_coupons_svip_plans_tenant_id_plan_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.CouponRedemption", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_coupon_redemptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.Coupon", null) - .WithMany() - .HasForeignKey("TenantId", "CouponId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_coupons_tenant_id_coupon_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_orders_tenant_id_order_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_coupon_redemptions_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Entitlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("RevokedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_entitlements_users_revoked_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entitlements_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Order", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_orders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_orders_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.SvipPlan", null) - .WithMany() - .HasForeignKey("TenantId", "PlanId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_svip_plans_tenant_id_plan_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_orders_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.OrderItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_order_items_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Payment", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Order", null) - .WithMany() - .HasForeignKey("TenantId", "OrderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payments_orders_tenant_id_order_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PaymentEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_payment_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Commerce.Payment", null) - .WithMany() - .HasForeignKey("TenantId", "PaymentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_payment_events_payments_tenant_id_payment_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityClaim", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_activity_claims_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_activity_claims_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.PointActivityTask", null) - .WithMany() - .HasForeignKey("TenantId", "TaskId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_point_activity_claims_point_activity_tasks_tenant_id_task_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityTask", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_activity_tasks_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_exchange_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_point_exchange_items_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeOrder", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_exchange_orders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_point_exchange_orders_users_user_id"); - - b.HasOne("Tiku.Domain.Commerce.PointExchangeItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_point_exchange_orders_point_exchange_items_tenant_id_item_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.Product", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_products_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_products_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.SvipPlan", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_svip_plans_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_svip_plans_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantPaymentAccount", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_payment_accounts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantSubscription", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_subscriptions_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Commerce.TenantUsageRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_usage_records_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AiRecommendationReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_ai_recommendation_reports_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_ai_recommendation_reports_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.AppAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_app_assets_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAsset", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_assets_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("VerifiedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_assets_users_verified_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_content_nodes_tenant_id_content_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_assets_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_asset_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_access_events_content_assets_tenant_id_asset_~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentAssetSecurityScanEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_asset_security_scan_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_asset_security_scan_events_content_assets_tenant_id~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentEntry", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_entries_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_entries_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportItem", null) - .WithMany() - .HasForeignKey("TenantId", "ItemId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_import_issues_content_import_items_tenant_id_item_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_issues_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentImportJob", null) - .WithMany() - .HasForeignKey("TenantId", "JobId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_items_content_import_jobs_tenant_id_job_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentImportJob", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_import_jobs_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_import_jobs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_categories_tenant_id_target_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "TargetContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_content_nodes_tenant_id_target_content_~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_question_banks_tenant_id_target_questio~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "TargetRegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_regions_tenant_id_target_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_import_jobs_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ContentNode", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_content_nodes_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_content_nodes_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ParentId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_content_nodes_content_nodes_tenant_id_parent_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_content_nodes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookChapter", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_chapters_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_chapters_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_handbook_chapters_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.HandbookSubject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_chapters_handbook_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookEntry", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_entries_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.HandbookChapter", null) - .WithMany() - .HasForeignKey("TenantId", "ChapterId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_handbook_entries_handbook_chapters_tenant_id_chapter_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_entries_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_handbook_entries_content_entries_tenant_id_entry_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.HandbookSubject", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_handbook_subjects_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_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_handbook_subjects_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "MajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_majors_tenant_id_major_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_handbook_subjects_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.ImageAsset", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_images_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.PracticeBlueprint", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_blueprints_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_blueprints_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_question_collections_tenant_id_collecti~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_practice_blueprints_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_bank_grants_question_banks_source_question_bank_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_collections_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "EntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_content_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_question_banks_tenant_id_question_bank~"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_collections_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollectionItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "CollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_question_collections_tenant_id_co~"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_collection_items_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_type_groups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_type_groups_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.QuestionVideo", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_videos_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.Content.VideoExplanation", null) - .WithMany() - .HasForeignKey("TenantId", "VideoId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_question_videos_video_explanations_tenant_id_video_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.SubjectShare", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SourceSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_source_subject_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "TargetSubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subject_shares_subjects_tenant_id_target_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_created_by"); - - b.HasOne("Tiku.Domain.Content.QuestionBankGrant", null) - .WithMany() - .HasForeignKey("GrantId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_bank_grants_grant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_source_questi~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_collections_tenant_~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "TargetEntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_content_entries_tenant_id_ta~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_tenant_id_tar~"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_favorites_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.UserWordProgress", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_users_user_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyWord", null) - .WithMany() - .HasForeignKey("TenantId", "WordId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_word_progress_vocabulary_words_tenant_id_word_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VideoExplanation", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_video_explanations_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_video_explanations_subjects_tenant_id_subject_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyUnit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_units_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_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_vocabulary_units_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_units_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Content.VocabularyWord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_vocabulary_words_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_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_vocabulary_words_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Content.VocabularyUnit", null) - .WithMany() - .HasForeignKey("TenantId", "UnitId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_vocabulary_words_vocabulary_units_tenant_id_unit_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlement", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GeneratedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_generated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PaidBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_paid_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlements_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementExportEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ExportedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_export_events_users_exported_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_export_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.CommissionSettlement", null) - .WithMany() - .HasForeignKey("TenantId", "SettlementId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_export_events_commission_settlements_~"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementItem", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.CommissionSettlement", null) - .WithMany() - .HasForeignKey("TenantId", "SettlementId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_items_commission_settlements_tenant_i~"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CommissionSettlementProof", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReviewedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_proofs_users_reviewed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("SubmittedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_proofs_users_submitted_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_proofs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Content.ContentAsset", null) - .WithMany() - .HasForeignKey("TenantId", "AssetId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_commission_settlement_proofs_content_assets_tenant_id_asset~"); - - b.HasOne("Tiku.Domain.Growth.CommissionSettlement", null) - .WithMany() - .HasForeignKey("TenantId", "SettlementId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_commission_settlement_proofs_commission_settlements_tenant_~"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmConfig", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_config_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookLog", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_log_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.CrmWebhookQueueItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_crm_webhook_queue_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_codes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralLead", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_leads_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralTrack", null) - .WithMany() - .HasForeignKey("TenantId", "FirstTrackId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_leads_referral_tracks_tenant_id_first_track_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralQrcode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_qrcodes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_qrcodes_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTeamEdge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("LeaderUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_team_edges_users_leader_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("MemberUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_users_member_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_team_edges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.ReferralTrack", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReferrerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_referrer_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("TargetUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_users_target_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_referral_tracks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Growth.ReferralLead", null) - .WithMany() - .HasForeignKey("TenantId", "LeadId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_referral_tracks_referral_leads_tenant_id_lead_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Growth.TenantCommissionSetting", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_commission_settings_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_commission_settings_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.StudentProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_student_profiles_users_user_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.Major", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedMajorId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_majors_tenant_id_selected_major_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SelectedSchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_student_profiles_schools_tenant_id_selected_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Identity.UserIdentity", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_identities_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_issues_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_pb_import_issues_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_import_runs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null) - .WithMany() - .HasForeignKey("TenantId", "RunId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_pb_raw_records_pb_import_runs_tenant_id_run_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_answer_records_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId", "QuestionVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_question_versions_tenant_id_question_id_ques~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "UserId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "UserId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_practice_sessions_tenant_id_user_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_dashboard_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_dashboard_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ExamDate", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_exam_dates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_regions_tenant_id_region_id"); - - b.HasOne("Tiku.Domain.Catalog.School", null) - .WithMany() - .HasForeignKey("TenantId", "SchoolId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_exam_dates_schools_tenant_id_school_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.FavoriteQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_favorite_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_access_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_users_user_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_practice_access_events_practice_sessions_tenant_id_practice~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeDailyUsage", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_daily_usage_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .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.PracticeSessionReport", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .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") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_reports_practice_sessions_tenant_id_practi~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReportSection", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.PracticeSession", null) - .WithMany() - .HasForeignKey("TenantId", "PracticeSessionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_sessions_tenant_i~"); - - b.HasOne("Tiku.Domain.Learning.PracticeSessionReport", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_practice_session_report_sections_practice_session_reports_t~"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RecentPractice", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_recent_practices_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_recent_practices_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.Report", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("HandledBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_handled_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_reports_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_reports_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_reports_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.ReportStatusEvent", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_report_status_events_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Learning.Report", null) - .WithMany() - .HasForeignKey("TenantId", "ReportId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_report_status_events_reports_tenant_id_report_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.RevenueDailyStat", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_revenue_daily_stats_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_revenue_daily_stats_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.UserScoreEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_score_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Learning.WrongQuestion", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_users_user_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_wrong_questions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_announcements_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.AuditLog", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ActorUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_audit_logs_users_actor_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_audit_logs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Badge", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_badges_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Banner", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_banners_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_banners_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.Faq", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_faqs_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_faqs_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantContentNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReadBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_content_notifications_users_read_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_content_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "SourceQuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_tenant_content_notifications_question_banks_tenant_id_sourc~"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.TenantThemeConfig", b => - { - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("ActiveTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_active_template~"); - - b.HasOne("Tiku.Domain.Operations.TenantThemeTemplate", null) - .WithMany() - .HasForeignKey("DraftTemplateCode") - .HasPrincipalKey("Code") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_tenant_theme_templates_draft_template_~"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("DraftUpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_draft_updated_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("PublishedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_theme_configs_users_published_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_theme_configs_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserBadge", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("GrantedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_badges_users_granted_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_badges_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_users_user_id"); - - b.HasOne("Tiku.Domain.Operations.Badge", null) - .WithMany() - .HasForeignKey("TenantId", "BadgeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_user_badges_badges_tenant_id_badge_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Operations.UserNotification", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_user_notifications_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_user_notifications_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlert", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AcknowledgedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_acknowledged_by"); - - b.HasOne("Tiku.Domain.Operations.AuditLog", null) - .WithMany() - .HasForeignKey("AuditLogId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_audit_logs_audit_log_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ResolvedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_platform_audit_alerts_users_resolved_by"); - - b.HasOne("Tiku.Domain.Platform.PlatformAuditAlertRule", null) - .WithMany() - .HasForeignKey("RuleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_audit_alerts_platform_audit_alert_rules_rule_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alerts_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformAuditAlertRule", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_platform_audit_alert_rules_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.PlatformDunningNotificationEvent", b => - { - b.HasOne("Tiku.Domain.Platform.PlatformDunningNotificationChannel", null) - .WithMany() - .HasForeignKey("ChannelId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_platform_dunning_notif~"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoices_tenant~"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoiceReminder", null) - .WithMany() - .HasForeignKey("TenantId", "ReminderId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_platform_dunning_notification_events_tenant_invoice_reminde~"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Platform.TenantBillingProfile", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_billing_profiles_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoice", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoices_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoices_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceItem", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_items_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoicePayment", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("ReceivedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_payments_users_received_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_payments_tenant_invoices_tenant_id_invoice_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Platform.TenantInvoiceReminder", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_invoice_reminders_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Platform.TenantInvoice", null) - .WithMany() - .HasForeignKey("TenantId", "InvoiceId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_invoice_reminders_tenant_invoices_tenant_id_invoice_~"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.Question", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_questions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Category", null) - .WithMany() - .HasForeignKey("TenantId", "CategoryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_categories_tenant_id_category_id"); - - b.HasOne("Tiku.Domain.Content.ContentNode", null) - .WithMany() - .HasForeignKey("TenantId", "ContentNodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_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_questions_content_entries_tenant_id_entry_id"); - - b.HasOne("Tiku.Domain.Catalog.ModuleNode", null) - .WithMany() - .HasForeignKey("TenantId", "NodeId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_module_nodes_tenant_id_node_id"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "PrimaryCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_collections_tenant_id_primary_collection~"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionBankId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_banks_tenant_id_question_bank_id"); - - b.HasOne("Tiku.Domain.Catalog.Subject", null) - .WithMany() - .HasForeignKey("TenantId", "SubjectId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_subjects_tenant_id_subject_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "Id", "CurrentVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_questions_question_versions_tenant_id_id_current_version_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionBank", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_banks_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_question_banks_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.QuestionBanks.QuestionVersion", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_versions_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_versions_questions_tenant_id_question_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthLoginEvent", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_login_events_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_auth_login_events_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.AuthSession", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_auth_sessions_users_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsSendRateLimit", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_send_rate_limits_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.SmsVerificationCode", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_sms_verification_codes_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.Tenant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("OwnerUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenants_users_owner_user_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantAuthProvider", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_auth_providers_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantBranding", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantBranding", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_branding_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClass", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_classes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_classes_users_updated_by"); - - b.HasOne("Tiku.Domain.Catalog.Region", null) - .WithMany() - .HasForeignKey("TenantId", "RegionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_classes_regions_tenant_id_region_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantClassMember", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_class_members_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantDomain", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_domains_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_memberships_users_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantRoleTemplate", null) - .WithMany() - .HasForeignKey("TenantId", "RoleTemplateId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantRoleTemplate", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_created_by"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_role_templates_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_role_templates_users_updated_by"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSecret", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_secrets_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantSettings", b => - { - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithOne() - .HasForeignKey("Tiku.Domain.Tenancy.TenantSettings", "TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_settings_tenants_tenant_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentFollowup", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("AssignedToUserId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_assigned_to_user_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CompletedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_users_completed_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_followups_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Tenancy.TenantClass", null) - .WithMany() - .HasForeignKey("TenantId", "ClassId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_followups_tenant_classes_tenant_id_class_id"); - }); - - modelBuilder.Entity("Tiku.Domain.Tenancy.TenantStudentNote", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_created_by"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("StudentUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_users_student_user_id"); - - b.HasOne("Tiku.Domain.Tenancy.Tenant", null) - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_student_notes_tenants_tenant_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_student_notes_users_updated_by"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260726122712_AddCommissionSettlementProofs.cs b/Tiku.Infrastructure/Persistence/Migrations/20260726122712_AddCommissionSettlementProofs.cs deleted file mode 100644 index d00a44c..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260726122712_AddCommissionSettlementProofs.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System; -using System.Text.Json; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - /// - public partial class AddCommissionSettlementProofs : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "commission_settlement_export_events", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - tenant_id = table.Column(type: "uuid", nullable: false), - settlement_id = table.Column(type: "uuid", nullable: false), - exported_by = table.Column(type: "uuid", nullable: true), - export_format = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), - filename = table.Column(type: "character varying(500)", maxLength: 500, nullable: false), - row_count = table.Column(type: "integer", nullable: false), - content_sha256 = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), - metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") - }, - constraints: table => - { - table.PrimaryKey("pk_commission_settlement_export_events", x => x.id); - table.CheckConstraint("ck_commission_export_events_rows", "row_count >= 0"); - table.ForeignKey( - name: "fk_commission_settlement_export_events_commission_settlements_~", - columns: x => new { x.tenant_id, x.settlement_id }, - principalTable: "commission_settlements", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "fk_commission_settlement_export_events_tenants_tenant_id", - column: x => x.tenant_id, - principalTable: "tenants", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "fk_commission_settlement_export_events_users_exported_by", - column: x => x.exported_by, - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.SetNull); - }); - - migrationBuilder.CreateTable( - name: "commission_settlement_proofs", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - settlement_id = table.Column(type: "uuid", nullable: false), - asset_id = table.Column(type: "uuid", nullable: true), - submitted_by = table.Column(type: "uuid", nullable: true), - reviewed_by = table.Column(type: "uuid", nullable: true), - proof_type = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - title = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), - description = table.Column(type: "text", nullable: true), - external_url = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), - amount_cents = table.Column(type: "integer", nullable: true), - payment_method = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), - payment_account = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), - paid_at = table.Column(type: "timestamp with time zone", nullable: true), - reviewed_at = table.Column(type: "timestamp with time zone", nullable: true), - review_note = table.Column(type: "text", nullable: true), - metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - tenant_id = table.Column(type: "uuid", nullable: false), - created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") - }, - constraints: table => - { - table.PrimaryKey("pk_commission_settlement_proofs", x => x.id); - table.UniqueConstraint("ak_commission_settlement_proofs_tenant_id_id", x => new { x.tenant_id, x.id }); - table.CheckConstraint("ck_commission_settlement_proofs_amount", "amount_cents is null or amount_cents >= 0"); - table.ForeignKey( - name: "fk_commission_settlement_proofs_commission_settlements_tenant_~", - columns: x => new { x.tenant_id, x.settlement_id }, - principalTable: "commission_settlements", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "fk_commission_settlement_proofs_content_assets_tenant_id_asset~", - columns: x => new { x.tenant_id, x.asset_id }, - principalTable: "content_assets", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.SetNull); - table.ForeignKey( - name: "fk_commission_settlement_proofs_tenants_tenant_id", - column: x => x.tenant_id, - principalTable: "tenants", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "fk_commission_settlement_proofs_users_reviewed_by", - column: x => x.reviewed_by, - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.SetNull); - table.ForeignKey( - name: "fk_commission_settlement_proofs_users_submitted_by", - column: x => x.submitted_by, - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.SetNull); - }); - - migrationBuilder.CreateIndex( - name: "ix_commission_settlement_export_events_exported_by", - table: "commission_settlement_export_events", - column: "exported_by"); - - migrationBuilder.CreateIndex( - name: "ix_commission_settlement_export_events_tenant_id_settlement_id~", - table: "commission_settlement_export_events", - columns: new[] { "tenant_id", "settlement_id", "created_at" }); - - migrationBuilder.CreateIndex( - name: "ix_commission_settlement_proofs_reviewed_by", - table: "commission_settlement_proofs", - column: "reviewed_by"); - - migrationBuilder.CreateIndex( - name: "ix_commission_settlement_proofs_submitted_by", - table: "commission_settlement_proofs", - column: "submitted_by"); - - migrationBuilder.CreateIndex( - name: "ix_commission_settlement_proofs_tenant_id_asset_id", - table: "commission_settlement_proofs", - columns: new[] { "tenant_id", "asset_id" }); - - migrationBuilder.CreateIndex( - name: "ix_commission_settlement_proofs_tenant_id_settlement_id_status~", - table: "commission_settlement_proofs", - columns: new[] { "tenant_id", "settlement_id", "status", "created_at" }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "commission_settlement_export_events"); - - migrationBuilder.DropTable( - name: "commission_settlement_proofs"); - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260727062859_EncryptTenantSecretPayloads.cs b/Tiku.Infrastructure/Persistence/Migrations/20260727062859_EncryptTenantSecretPayloads.cs deleted file mode 100644 index a28bf28..0000000 --- a/Tiku.Infrastructure/Persistence/Migrations/20260727062859_EncryptTenantSecretPayloads.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Text.Json; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Tiku.Infrastructure.Persistence.Migrations -{ - /// - public partial class EncryptTenantSecretPayloads : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "secret_payload", - table: "tenant_secrets"); - - migrationBuilder.AddColumn( - name: "encrypted_payload", - table: "tenant_secrets", - type: "bytea", - nullable: false, - defaultValue: new byte[0]); - - migrationBuilder.AddColumn( - name: "encryption_key_id", - table: "tenant_secrets", - type: "character varying(100)", - maxLength: 100, - nullable: false, - defaultValue: ""); - - migrationBuilder.AddColumn( - name: "encryption_nonce", - table: "tenant_secrets", - type: "bytea", - nullable: false, - defaultValue: new byte[0]); - - migrationBuilder.AddColumn( - name: "encryption_tag", - table: "tenant_secrets", - type: "bytea", - nullable: false, - defaultValue: new byte[0]); - - migrationBuilder.AddCheckConstraint( - name: "ck_tenant_secrets_encryption_envelope", - table: "tenant_secrets", - sql: "octet_length(encrypted_payload) > 0 and octet_length(encryption_nonce) = 12 and octet_length(encryption_tag) = 16 and encryption_key_id <> ''"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropCheckConstraint( - name: "ck_tenant_secrets_encryption_envelope", - table: "tenant_secrets"); - - migrationBuilder.DropColumn( - name: "encrypted_payload", - table: "tenant_secrets"); - - migrationBuilder.DropColumn( - name: "encryption_key_id", - table: "tenant_secrets"); - - migrationBuilder.DropColumn( - name: "encryption_nonce", - table: "tenant_secrets"); - - migrationBuilder.DropColumn( - name: "encryption_tag", - table: "tenant_secrets"); - - migrationBuilder.AddColumn( - name: "secret_payload", - table: "tenant_secrets", - type: "jsonb", - nullable: false, - defaultValueSql: "'{}'::jsonb"); - } - } -} diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260727062859_EncryptTenantSecretPayloads.Designer.cs b/Tiku.Infrastructure/Persistence/Migrations/20260727084726_InitialSchema.Designer.cs similarity index 96% rename from Tiku.Infrastructure/Persistence/Migrations/20260727062859_EncryptTenantSecretPayloads.Designer.cs rename to Tiku.Infrastructure/Persistence/Migrations/20260727084726_InitialSchema.Designer.cs index 6093181..09865d1 100644 --- a/Tiku.Infrastructure/Persistence/Migrations/20260727062859_EncryptTenantSecretPayloads.Designer.cs +++ b/Tiku.Infrastructure/Persistence/Migrations/20260727084726_InitialSchema.Designer.cs @@ -13,8 +13,8 @@ using Tiku.Infrastructure.Persistence; namespace Tiku.Infrastructure.Persistence.Migrations { [DbContext(typeof(TikuDbContext))] - [Migration("20260727062859_EncryptTenantSecretPayloads")] - partial class EncryptTenantSecretPayloads + [Migration("20260727084726_InitialSchema")] + partial class InitialSchema { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -289,6 +289,56 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.ToTable("module_nodes", (string)null); }); + modelBuilder.Entity("Tiku.Domain.Catalog.QuestionTaxonomyAssignment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now()"); + + b.Property("IsPrimary") + .HasColumnType("boolean") + .HasColumnName("is_primary"); + + b.Property("QuestionId") + .HasColumnType("uuid") + .HasColumnName("question_id"); + + b.Property("TaxonomyNodeId") + .HasColumnType("uuid") + .HasColumnName("taxonomy_node_id"); + + b.Property("TaxonomyOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("taxonomy_owner_tenant_id"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("tenant_id"); + + b.HasKey("Id") + .HasName("pk_question_taxonomy_assignments"); + + b.HasAlternateKey("TenantId", "Id") + .HasName("ak_question_taxonomy_assignments_tenant_id_id"); + + b.HasIndex("TaxonomyOwnerTenantId", "TaxonomyNodeId") + .HasDatabaseName("ix_question_taxonomy_assignments_taxonomy_owner_tenant_id_taxo~"); + + b.HasIndex("TenantId", "QuestionId", "TaxonomyOwnerTenantId", "TaxonomyNodeId") + .IsUnique() + .HasDatabaseName("ix_question_taxonomy_assignments_tenant_id_question_id_taxonom~"); + + b.ToTable("question_taxonomy_assignments", (string)null); + }); + modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => { b.Property("Id") @@ -864,6 +914,97 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.ToTable("subjects", (string)null); }); + modelBuilder.Entity("Tiku.Domain.Catalog.TaxonomyNode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("code"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now()"); + + b.Property("Depth") + .HasColumnType("integer") + .HasColumnName("depth"); + + b.Property("IsActive") + .HasColumnType("boolean") + .HasColumnName("is_active"); + + b.Property("Metadata") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("metadata") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("name"); + + b.Property("NodeType") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("node_type"); + + b.Property("ParentId") + .HasColumnType("uuid") + .HasColumnName("parent_id"); + + b.Property("ParentOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("parent_owner_tenant_id"); + + b.Property("Path") + .HasColumnType("ltree") + .HasColumnName("path"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasColumnName("sort_order"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("tenant_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now()"); + + b.HasKey("Id") + .HasName("pk_taxonomy_nodes"); + + b.HasAlternateKey("TenantId", "Id") + .HasName("ak_taxonomy_nodes_tenant_id_id"); + + b.HasIndex("ParentOwnerTenantId", "ParentId") + .HasDatabaseName("ix_taxonomy_nodes_parent_owner_tenant_id_parent_id"); + + b.HasIndex("TenantId", "Code") + .IsUnique() + .HasDatabaseName("ix_taxonomy_nodes_tenant_id_code"); + + b.ToTable("taxonomy_nodes", null, t => + { + t.HasCheckConstraint("ck_taxonomy_nodes_parent_pair", "(parent_owner_tenant_id is null) = (parent_id is null)"); + }); + }); + modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => { b.Property("Id") @@ -1641,10 +1782,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_commerce_reconciliation_items_tenant_id_id"); - b.HasIndex("BatchId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_items_batch_id_row_no"); - b.HasIndex("TenantId", "OrderId") .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_id"); @@ -1654,6 +1791,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("TenantId", "RefundRequestId") .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_request_id"); + b.HasIndex("TenantId", "BatchId", "RowNo") + .IsUnique() + .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_batch_id_row_no"); + b.HasIndex("TenantId", "OrderNo", "CreatedAt") .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_no_created_at"); @@ -2524,11 +2665,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_payments_tenant_id_id"); - b.HasIndex("Provider", "ProviderTradeNo") - .IsUnique() - .HasDatabaseName("ix_payments_provider_provider_trade_no") - .HasFilter("provider_trade_no is not null"); - b.HasIndex("TenantId", "LegacyId") .IsUnique() .HasDatabaseName("ix_payments_tenant_id_legacy_id"); @@ -2536,6 +2672,11 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("TenantId", "OrderId", "UpdatedAt") .HasDatabaseName("ix_payments_tenant_id_order_id_updated_at"); + b.HasIndex("TenantId", "Provider", "ProviderTradeNo") + .IsUnique() + .HasDatabaseName("ix_payments_tenant_id_provider_provider_trade_no") + .HasFilter("provider_trade_no is not null"); + b.ToTable("payments", null, t => { t.HasCheckConstraint("ck_payments_amount", "amount_cents >= 0"); @@ -2607,13 +2748,13 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_payment_events_tenant_id_id"); - b.HasIndex("Provider", "EventId") - .IsUnique() - .HasDatabaseName("ix_payment_events_provider_event_id"); - b.HasIndex("TenantId", "PaymentId") .HasDatabaseName("ix_payment_events_tenant_id_payment_id"); + b.HasIndex("TenantId", "Provider", "EventId") + .IsUnique() + .HasDatabaseName("ix_payment_events_tenant_id_provider_event_id"); + b.ToTable("payment_events", (string)null); }); @@ -4387,9 +4528,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_content_import_items_tenant_id_id"); - b.HasIndex("JobId", "RowNo") + b.HasIndex("TenantId", "JobId", "RowNo") .IsUnique() - .HasDatabaseName("ix_content_import_items_job_id_row_no"); + .HasDatabaseName("ix_content_import_items_tenant_id_job_id_row_no"); b.HasIndex("TenantId", "JobId", "Status", "RowNo") .HasDatabaseName("ix_content_import_items_tenant_id_job_id_status_row_no"); @@ -5279,117 +5420,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations }); }); - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("AllowedPlanCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("allowed_plan_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.PrimitiveCollection("AllowedRegionIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_region_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedSubjectIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_subject_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedTenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("GrantScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("grant_scope"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_bank_grants"); - - b.HasIndex("AllowedPlanCodes") - .HasDatabaseName("ix_question_bank_grants_allowed_plan_codes"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedPlanCodes"), "gin"); - - b.HasIndex("AllowedTenantIds") - .HasDatabaseName("ix_question_bank_grants_allowed_tenant_ids"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedTenantIds"), "gin"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_bank_grants_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_bank_grants_updated_by"); - - b.HasIndex("SourceQuestionBankId", "Status", "StartsAt", "ExpiresAt") - .HasDatabaseName("ix_question_bank_grants_source_question_bank_id_status_starts_~"); - - b.ToTable("question_bank_grants", (string)null); - }); - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => { b.Property("Id") @@ -5580,6 +5610,14 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("question_id"); + b.Property("QuestionOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("question_owner_tenant_id"); + + b.Property("QuestionReferenceId") + .HasColumnType("uuid") + .HasColumnName("question_reference_id"); + b.Property("Required") .HasColumnType("boolean") .HasColumnName("required"); @@ -5614,10 +5652,13 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_question_collection_items_tenant_id_id"); - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_question_collection_items_tenant_id_question_id"); + b.HasIndex("QuestionOwnerTenantId", "QuestionId") + .HasDatabaseName("ix_question_collection_items_question_owner_tenant_id_question~"); - b.HasIndex("TenantId", "CollectionId", "QuestionId") + b.HasIndex("TenantId", "QuestionReferenceId") + .HasDatabaseName("ix_question_collection_items_tenant_id_question_reference_id"); + + b.HasIndex("TenantId", "CollectionId", "QuestionReferenceId") .IsUnique() .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_question_~"); @@ -5828,7 +5869,7 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.ToTable("subject_shares", (string)null); }); - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => + modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankPreference", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -5836,15 +5877,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("id") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("AdoptionMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("adoption_mode"); - - b.Property("CopiedQuestionCount") - .HasColumnType("integer") - .HasColumnName("copied_question_count"); + b.Property("Alias") + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("alias"); b.Property("CreatedAt") .ValueGeneratedOnAdd() @@ -5856,13 +5892,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("created_by"); - b.Property("GrantId") - .HasColumnType("uuid") - .HasColumnName("grant_id"); - - b.Property("LastSyncedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_synced_at"); + b.Property("IsVisible") + .HasColumnType("boolean") + .HasColumnName("is_visible"); b.Property("Metadata") .ValueGeneratedOnAdd() @@ -5870,39 +5902,22 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("metadata") .HasDefaultValueSql("'{}'::jsonb"); - b.Property("SourceQuestionBankId") + b.Property("NavigationLocation") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("navigation_location"); + + b.Property("QuestionBankId") .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); + .HasColumnName("question_bank_id"); - b.Property("SourceSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SyncStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("sync_status"); - - b.Property("TargetCollectionId") + b.Property("QuestionBankOwnerTenantId") .HasColumnType("uuid") - .HasColumnName("target_collection_id"); + .HasColumnName("question_bank_owner_tenant_id"); - b.Property("TargetEntryId") - .HasColumnType("uuid") - .HasColumnName("target_entry_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); + b.Property("SortOrder") + .HasColumnType("integer") + .HasColumnName("sort_order"); b.Property("TenantId") .HasColumnType("uuid") @@ -5919,43 +5934,85 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("updated_by"); b.HasKey("Id") - .HasName("pk_tenant_question_bank_adoptions"); + .HasName("pk_tenant_question_bank_preferences"); b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_question_bank_adoptions_tenant_id_id"); + .HasName("ak_tenant_question_bank_preferences_tenant_id_id"); b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_created_by"); - - b.HasIndex("GrantId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_grant_id"); - - b.HasIndex("SourceQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_source_question_bank_id"); + .HasDatabaseName("ix_tenant_question_bank_preferences_created_by"); b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_updated_by"); + .HasDatabaseName("ix_tenant_question_bank_preferences_updated_by"); - b.HasIndex("TenantId", "SourceQuestionBankId") + b.HasIndex("QuestionBankOwnerTenantId", "QuestionBankId") + .HasDatabaseName("ix_tenant_question_bank_preferences_question_bank_owner_tenant~"); + + b.HasIndex("TenantId", "QuestionBankOwnerTenantId", "QuestionBankId") .IsUnique() - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_source_question_ba~"); + .HasDatabaseName("ix_tenant_question_bank_preferences_tenant_id_question_bank_ow~"); - b.HasIndex("TenantId", "TargetCollectionId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_collection_~"); + b.ToTable("tenant_question_bank_preferences", (string)null); + }); - b.HasIndex("TenantId", "TargetEntryId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_entry_id"); + modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionReference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_question_ba~"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now()"); - b.HasIndex("TenantId", "Status", "UpdatedAt") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_status_updated_at"); + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); - b.ToTable("tenant_question_bank_adoptions", null, t => - { - t.HasCheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0"); - }); + b.Property("QuestionId") + .HasColumnType("uuid") + .HasColumnName("question_id"); + + b.Property("QuestionOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("question_owner_tenant_id"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("source"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("tenant_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now()"); + + b.HasKey("Id") + .HasName("pk_tenant_question_references"); + + b.HasAlternateKey("TenantId", "Id") + .HasName("ak_tenant_question_references_tenant_id_id"); + + b.HasAlternateKey("TenantId", "QuestionOwnerTenantId", "QuestionId") + .HasName("ak_tenant_question_references_tenant_id_question_owner_tenant_~"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("ix_tenant_question_references_created_by"); + + b.HasIndex("QuestionOwnerTenantId", "QuestionId") + .HasDatabaseName("ix_tenant_question_references_question_owner_tenant_id_questio~"); + + b.ToTable("tenant_question_references", (string)null); }); modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => @@ -8328,24 +8385,20 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("character varying(64)") .HasColumnName("legacy_question_id"); - b.Property("PracticeSessionId") + b.Property("PracticeSessionId") .HasColumnType("uuid") .HasColumnName("practice_session_id"); - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("QuestionVersionId") - .HasColumnType("uuid") - .HasColumnName("question_version_id"); - b.Property("SelectedOptions") .ValueGeneratedOnAdd() .HasColumnType("jsonb") .HasColumnName("selected_options") .HasDefaultValueSql("'[]'::jsonb"); + b.Property("SessionQuestionId") + .HasColumnType("uuid") + .HasColumnName("session_question_id"); + b.Property("TenantId") .HasColumnType("uuid") .HasColumnName("tenant_id"); @@ -8367,19 +8420,13 @@ namespace Tiku.Infrastructure.Persistence.Migrations .IsUnique() .HasDatabaseName("ix_answer_records_tenant_id_legacy_id"); - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id"); - - b.HasIndex("TenantId", "QuestionId", "QuestionVersionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id_question_version_id"); + b.HasIndex("TenantId", "PracticeSessionId", "SessionQuestionId") + .HasDatabaseName("ix_answer_records_tenant_id_practice_session_id_session_questi~"); b.HasIndex("TenantId", "UserId", "PracticeSessionId") .HasDatabaseName("ix_answer_records_tenant_id_user_id_practice_session_id"); - b.ToTable("answer_records", null, t => - { - t.HasCheckConstraint("ck_answer_records_version_requires_question", "question_version_id is null or question_id is not null"); - }); + b.ToTable("answer_records", (string)null); }); modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => @@ -8569,9 +8616,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("user_id"); - b.Property("QuestionId") + b.Property("QuestionReferenceId") .HasColumnType("uuid") - .HasColumnName("question_id"); + .HasColumnName("question_reference_id"); b.Property("CreatedAt") .ValueGeneratedOnAdd() @@ -8579,20 +8626,31 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("created_at") .HasDefaultValueSql("now()"); + b.Property("QuestionId") + .HasColumnType("uuid") + .HasColumnName("question_id"); + + b.Property("QuestionOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("question_owner_tenant_id"); + b.Property("Source") .IsRequired() .HasMaxLength(50) .HasColumnType("character varying(50)") .HasColumnName("source"); - b.HasKey("TenantId", "UserId", "QuestionId") + b.HasKey("TenantId", "UserId", "QuestionReferenceId") .HasName("pk_favorite_questions"); b.HasIndex("UserId") .HasDatabaseName("ix_favorite_questions_user_id"); - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_favorite_questions_tenant_id_question_id"); + b.HasIndex("QuestionOwnerTenantId", "QuestionId") + .HasDatabaseName("ix_favorite_questions_question_owner_tenant_id_question_id"); + + b.HasIndex("TenantId", "QuestionReferenceId") + .HasDatabaseName("ix_favorite_questions_tenant_id_question_reference_id"); b.ToTable("favorite_questions", (string)null); }); @@ -8854,12 +8912,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("integer") .HasColumnName("question_count"); - b.Property("QuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - b.Property("StartedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") @@ -8921,6 +8973,69 @@ namespace Tiku.Infrastructure.Persistence.Migrations }); }); + modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Position") + .HasColumnType("integer") + .HasColumnName("position"); + + b.Property("PracticeSessionId") + .HasColumnType("uuid") + .HasColumnName("practice_session_id"); + + b.Property("QuestionId") + .HasColumnType("uuid") + .HasColumnName("question_id"); + + b.Property("QuestionOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("question_owner_tenant_id"); + + b.Property("QuestionReferenceId") + .HasColumnType("uuid") + .HasColumnName("question_reference_id"); + + b.Property("QuestionVersionId") + .HasColumnType("uuid") + .HasColumnName("question_version_id"); + + b.Property("Score") + .HasPrecision(8, 2) + .HasColumnType("numeric(8,2)") + .HasColumnName("score"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("tenant_id"); + + b.HasKey("Id") + .HasName("pk_practice_session_questions"); + + b.HasAlternateKey("TenantId", "Id") + .HasName("ak_practice_session_questions_tenant_id_id"); + + b.HasAlternateKey("TenantId", "PracticeSessionId", "Id") + .HasName("ak_practice_session_questions_tenant_id_practice_session_id_id"); + + b.HasIndex("QuestionOwnerTenantId", "QuestionId", "QuestionVersionId") + .HasDatabaseName("ix_practice_session_questions_question_owner_tenant_id_questio~"); + + b.HasIndex("TenantId", "PracticeSessionId", "Position") + .IsUnique() + .HasDatabaseName("ix_practice_session_questions_tenant_id_practice_session_id_po~"); + + b.HasIndex("TenantId", "QuestionOwnerTenantId", "QuestionId") + .HasDatabaseName("ix_practice_session_questions_tenant_id_question_owner_tenant_~"); + + b.ToTable("practice_session_questions", (string)null); + }); + modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReport", b => { b.Property("Id") @@ -9651,9 +9766,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("user_id"); - b.Property("QuestionId") + b.Property("QuestionReferenceId") .HasColumnType("uuid") - .HasColumnName("question_id"); + .HasColumnName("question_reference_id"); b.Property("LastWrongAt") .ValueGeneratedOnAdd() @@ -9661,6 +9776,14 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("last_wrong_at") .HasDefaultValueSql("now()"); + b.Property("QuestionId") + .HasColumnType("uuid") + .HasColumnName("question_id"); + + b.Property("QuestionOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("question_owner_tenant_id"); + b.Property("ResolvedAt") .HasColumnType("timestamp with time zone") .HasColumnName("resolved_at"); @@ -9671,14 +9794,17 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasDefaultValue(1) .HasColumnName("wrong_count"); - b.HasKey("TenantId", "UserId", "QuestionId") + b.HasKey("TenantId", "UserId", "QuestionReferenceId") .HasName("pk_wrong_questions"); b.HasIndex("UserId") .HasDatabaseName("ix_wrong_questions_user_id"); - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_wrong_questions_tenant_id_question_id"); + b.HasIndex("QuestionOwnerTenantId", "QuestionId") + .HasDatabaseName("ix_wrong_questions_question_owner_tenant_id_question_id"); + + b.HasIndex("TenantId", "QuestionReferenceId") + .HasDatabaseName("ix_wrong_questions_tenant_id_question_reference_id"); b.ToTable("wrong_questions", (string)null); }); @@ -11016,9 +11142,8 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_platform_dunning_notification_events_tenant_id_id"); - b.HasIndex("ChannelId", "ReminderId") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_events_channel_id_reminder_id"); + b.HasIndex("ChannelId") + .HasDatabaseName("ix_platform_dunning_notification_events_channel_id"); b.HasIndex("TenantId", "InvoiceId") .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_invoice_id"); @@ -11032,6 +11157,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("ReminderId", "Status", "CreatedAt") .HasDatabaseName("ix_platform_dunning_notification_events_reminder_id_status_cre~"); + b.HasIndex("TenantId", "ChannelId", "ReminderId") + .IsUnique() + .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_channel_id_r~"); + b.HasIndex("TenantId", "Status", "CreatedAt") .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_status_creat~"); @@ -11334,9 +11463,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("CreatedBy") .HasDatabaseName("ix_tenant_invoices_created_by"); - b.HasIndex("InvoiceNo") + b.HasIndex("TenantId", "InvoiceNo") .IsUnique() - .HasDatabaseName("ix_tenant_invoices_invoice_no"); + .HasDatabaseName("ix_tenant_invoices_tenant_id_invoice_no"); b.HasIndex("TenantId", "BillingPeriodStart", "BillingPeriodEnd") .IsUnique() @@ -11511,10 +11640,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_tenant_invoice_payments_tenant_id_id"); - b.HasIndex("PaymentNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_payments_payment_no"); - b.HasIndex("ReceivedBy") .HasDatabaseName("ix_tenant_invoice_payments_received_by"); @@ -11524,6 +11649,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("TenantId", "InvoiceId") .HasDatabaseName("ix_tenant_invoice_payments_tenant_id_invoice_id"); + b.HasIndex("TenantId", "PaymentNo") + .IsUnique() + .HasDatabaseName("ix_tenant_invoice_payments_tenant_id_payment_no"); + b.ToTable("tenant_invoice_payments", null, t => { t.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0"); @@ -11826,12 +11955,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("region_id"); - b.Property("SourceScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_scope"); - b.Property("Status") .IsRequired() .HasMaxLength(32) @@ -11950,9 +12073,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("CreatedBy") .HasDatabaseName("ix_question_versions_created_by"); - b.HasIndex("QuestionId", "VersionNo") + b.HasIndex("TenantId", "QuestionId", "VersionNo") .IsUnique() - .HasDatabaseName("ix_question_versions_question_id_version_no"); + .HasDatabaseName("ix_question_versions_tenant_id_question_id_version_no"); b.ToTable("question_versions", (string)null); }); @@ -12104,7 +12227,8 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("TokenHash") .IsUnique() - .HasDatabaseName("ix_auth_sessions_token_hash"); + .HasDatabaseName("ix_auth_sessions_token_hash") + .HasAnnotation("Tiku:GlobalUnique", true); b.HasIndex("UserId") .HasDatabaseName("ix_auth_sessions_user_id"); @@ -12337,6 +12461,11 @@ namespace Tiku.Infrastructure.Persistence.Migrations .IsUnique() .HasDatabaseName("ix_tenants_legacy_id"); + b.HasIndex("Mode") + .IsUnique() + .HasDatabaseName("ix_tenants_mode") + .HasFilter("mode = 'platform_owned'"); + b.HasIndex("OwnerUserId") .HasDatabaseName("ix_tenants_owner_user_id"); @@ -12694,6 +12823,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("created_at") .HasDefaultValueSql("now()"); + b.Property("DnsVerifiedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("dns_verified_at"); + b.Property("DomainType") .IsRequired() .HasMaxLength(32) @@ -12710,6 +12843,15 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("boolean") .HasColumnName("is_primary"); + b.Property("LastCheckedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_checked_at"); + + b.Property("LastFailureReason") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("last_failure_reason"); + b.Property("Status") .IsRequired() .HasMaxLength(32) @@ -12720,6 +12862,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("tenant_id"); + b.Property("TlsReadyAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("tls_ready_at"); + b.Property("UpdatedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") @@ -12743,7 +12889,8 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("Host") .IsUnique() - .HasDatabaseName("ix_tenant_domains_host"); + .HasDatabaseName("ix_tenant_domains_host") + .HasAnnotation("Tiku:GlobalUnique", true); b.HasIndex("TenantId", "IsPrimary") .IsUnique() @@ -12753,6 +12900,121 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.ToTable("tenant_domains", (string)null); }); + modelBuilder.Entity("Tiku.Domain.Tenancy.TenantFrontendConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ConfigVersion") + .IsConcurrencyToken() + .HasColumnType("integer") + .HasColumnName("config_version"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now()"); + + b.Property("DraftBranding") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("draft_branding") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("DraftFeatures") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("draft_features") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("DraftHomeModules") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("draft_home_modules") + .HasDefaultValueSql("'[]'::jsonb"); + + b.Property("DraftNavigation") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("draft_navigation") + .HasDefaultValueSql("'[]'::jsonb"); + + b.Property("DraftTheme") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("draft_theme") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("PublishedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("published_at"); + + b.Property("PublishedBranding") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("published_branding") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("PublishedFeatures") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("published_features") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("PublishedHomeModules") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("published_home_modules") + .HasDefaultValueSql("'[]'::jsonb"); + + b.Property("PublishedNavigation") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("published_navigation") + .HasDefaultValueSql("'[]'::jsonb"); + + b.Property("PublishedTheme") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("published_theme") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("SchemaVersion") + .HasColumnType("integer") + .HasColumnName("schema_version"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("tenant_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now()"); + + b.HasKey("Id") + .HasName("pk_tenant_frontend_configs"); + + b.HasAlternateKey("TenantId", "Id") + .HasName("ak_tenant_frontend_configs_tenant_id_id"); + + b.HasIndex("TenantId") + .IsUnique() + .HasDatabaseName("ix_tenant_frontend_configs_tenant_id"); + + b.ToTable("tenant_frontend_configs", null, t => + { + t.HasCheckConstraint("ck_tenant_frontend_configs_config_version", "config_version > 0"); + + t.HasCheckConstraint("ck_tenant_frontend_configs_schema_version", "schema_version > 0"); + }); + }); + modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => { b.Property("Id") @@ -13381,6 +13643,25 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_module_nodes_regions_tenant_id_region_id"); }); + modelBuilder.Entity("Tiku.Domain.Catalog.QuestionTaxonomyAssignment", b => + { + b.HasOne("Tiku.Domain.Catalog.TaxonomyNode", null) + .WithMany() + .HasForeignKey("TaxonomyOwnerTenantId", "TaxonomyNodeId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_question_taxonomy_assignments_taxonomy_nodes_taxonomy_owner~"); + + b.HasOne("Tiku.Domain.QuestionBanks.Question", null) + .WithMany() + .HasForeignKey("TenantId", "QuestionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_question_taxonomy_assignments_questions_tenant_id_question_~"); + }); + modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => { b.HasOne("Tiku.Domain.Tenancy.Tenant", null) @@ -13525,6 +13806,23 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_subjects_schools_tenant_id_school_id"); }); + modelBuilder.Entity("Tiku.Domain.Catalog.TaxonomyNode", b => + { + b.HasOne("Tiku.Domain.Tenancy.Tenant", null) + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_taxonomy_nodes_tenants_tenant_id"); + + b.HasOne("Tiku.Domain.Catalog.TaxonomyNode", null) + .WithMany() + .HasForeignKey("ParentOwnerTenantId", "ParentId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_taxonomy_nodes_taxonomy_nodes_parent_owner_tenant_id_parent~"); + }); + modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => { b.HasOne("Tiku.Domain.Identity.User", null) @@ -14559,28 +14857,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_practice_blueprints_regions_tenant_id_region_id"); }); - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_bank_grants_question_banks_source_question_bank_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_updated_by"); - }); - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => { b.HasOne("Tiku.Domain.Identity.User", null) @@ -14654,6 +14930,14 @@ namespace Tiku.Infrastructure.Persistence.Migrations .IsRequired() .HasConstraintName("fk_question_collection_items_tenants_tenant_id"); + b.HasOne("Tiku.Domain.QuestionBanks.Question", null) + .WithMany() + .HasForeignKey("QuestionOwnerTenantId", "QuestionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_question_collection_items_questions_question_owner_tenant_i~"); + b.HasOne("Tiku.Domain.Content.QuestionCollection", null) .WithMany() .HasForeignKey("TenantId", "CollectionId") @@ -14662,13 +14946,13 @@ namespace Tiku.Infrastructure.Persistence.Migrations .IsRequired() .HasConstraintName("fk_question_collection_items_question_collections_tenant_id_co~"); - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) + b.HasOne("Tiku.Domain.Content.TenantQuestionReference", null) .WithMany() - .HasForeignKey("TenantId", "QuestionId") + .HasForeignKey("TenantId", "QuestionReferenceId") .HasPrincipalKey("TenantId", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired() - .HasConstraintName("fk_question_collection_items_questions_tenant_id_question_id"); + .HasConstraintName("fk_question_collection_items_tenant_question_references_tenant~"); }); modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => @@ -14738,60 +15022,58 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_subject_shares_subjects_tenant_id_target_subject_id"); }); - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => + modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankPreference", b => { b.HasOne("Tiku.Domain.Identity.User", null) .WithMany() .HasForeignKey("CreatedBy") .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_created_by"); - - b.HasOne("Tiku.Domain.Content.QuestionBankGrant", null) - .WithMany() - .HasForeignKey("GrantId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_bank_grants_grant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_source_questi~"); + .HasConstraintName("fk_tenant_question_bank_preferences_users_created_by"); b.HasOne("Tiku.Domain.Tenancy.Tenant", null) .WithMany() .HasForeignKey("TenantId") .OnDelete(DeleteBehavior.Cascade) .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_tenants_tenant_id"); + .HasConstraintName("fk_tenant_question_bank_preferences_tenants_tenant_id"); b.HasOne("Tiku.Domain.Identity.User", null) .WithMany() .HasForeignKey("UpdatedBy") .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_collections_tenant_~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "TargetEntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_content_entries_tenant_id_ta~"); + .HasConstraintName("fk_tenant_question_bank_preferences_users_updated_by"); b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") + .HasForeignKey("QuestionBankOwnerTenantId", "QuestionBankId") .HasPrincipalKey("TenantId", "Id") .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_tenant_id_tar~"); + .IsRequired() + .HasConstraintName("fk_tenant_question_bank_preferences_question_banks_question_ba~"); + }); + + modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionReference", b => + { + b.HasOne("Tiku.Domain.Identity.User", null) + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("fk_tenant_question_references_users_created_by"); + + b.HasOne("Tiku.Domain.Tenancy.Tenant", null) + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tenant_question_references_tenants_tenant_id"); + + b.HasOne("Tiku.Domain.QuestionBanks.Question", null) + .WithMany() + .HasForeignKey("QuestionOwnerTenantId", "QuestionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_tenant_question_references_questions_question_owner_tenant_~"); }); modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => @@ -15331,25 +15613,20 @@ namespace Tiku.Infrastructure.Persistence.Migrations .IsRequired() .HasConstraintName("fk_answer_records_users_user_id"); - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) + b.HasOne("Tiku.Domain.Learning.PracticeSessionQuestion", null) .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") + .HasForeignKey("TenantId", "PracticeSessionId", "SessionQuestionId") + .HasPrincipalKey("TenantId", "PracticeSessionId", "Id") .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId", "QuestionVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_question_versions_tenant_id_question_id_ques~"); + .IsRequired() + .HasConstraintName("fk_answer_records_practice_session_questions_tenant_id_practic~"); b.HasOne("Tiku.Domain.Learning.PracticeSession", null) .WithMany() .HasForeignKey("TenantId", "UserId", "PracticeSessionId") .HasPrincipalKey("TenantId", "UserId", "Id") .OnDelete(DeleteBehavior.Restrict) + .IsRequired() .HasConstraintName("fk_answer_records_practice_sessions_tenant_id_user_id_practice~"); }); @@ -15412,11 +15689,19 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasOne("Tiku.Domain.QuestionBanks.Question", null) .WithMany() - .HasForeignKey("TenantId", "QuestionId") + .HasForeignKey("QuestionOwnerTenantId", "QuestionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_favorite_questions_questions_question_owner_tenant_id_quest~"); + + b.HasOne("Tiku.Domain.Content.TenantQuestionReference", null) + .WithMany() + .HasForeignKey("TenantId", "QuestionReferenceId") .HasPrincipalKey("TenantId", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired() - .HasConstraintName("fk_favorite_questions_questions_tenant_id_question_id"); + .HasConstraintName("fk_favorite_questions_tenant_question_references_tenant_id_que~"); }); modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => @@ -15504,6 +15789,40 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_practice_sessions_content_entries_tenant_id_entry_id"); }); + modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionQuestion", b => + { + b.HasOne("Tiku.Domain.Tenancy.Tenant", null) + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_practice_session_questions_tenants_tenant_id"); + + b.HasOne("Tiku.Domain.Learning.PracticeSession", null) + .WithMany() + .HasForeignKey("TenantId", "PracticeSessionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_practice_session_questions_practice_sessions_tenant_id_prac~"); + + b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) + .WithMany() + .HasForeignKey("QuestionOwnerTenantId", "QuestionId", "QuestionVersionId") + .HasPrincipalKey("TenantId", "QuestionId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_practice_session_questions_question_versions_question_owner~"); + + b.HasOne("Tiku.Domain.Content.TenantQuestionReference", null) + .WithMany() + .HasForeignKey("TenantId", "QuestionOwnerTenantId", "QuestionId") + .HasPrincipalKey("TenantId", "QuestionOwnerTenantId", "QuestionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_practice_session_questions_tenant_question_references_tenan~"); + }); + modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReport", b => { b.HasOne("Tiku.Domain.Tenancy.Tenant", null) @@ -15690,11 +16009,19 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasOne("Tiku.Domain.QuestionBanks.Question", null) .WithMany() - .HasForeignKey("TenantId", "QuestionId") + .HasForeignKey("QuestionOwnerTenantId", "QuestionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_wrong_questions_questions_question_owner_tenant_id_question~"); + + b.HasOne("Tiku.Domain.Content.TenantQuestionReference", null) + .WithMany() + .HasForeignKey("TenantId", "QuestionReferenceId") .HasPrincipalKey("TenantId", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired() - .HasConstraintName("fk_wrong_questions_questions_tenant_id_question_id"); + .HasConstraintName("fk_wrong_questions_tenant_question_references_tenant_id_questi~"); }); modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => @@ -16298,6 +16625,16 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_tenant_domains_tenants_tenant_id"); }); + modelBuilder.Entity("Tiku.Domain.Tenancy.TenantFrontendConfig", b => + { + b.HasOne("Tiku.Domain.Tenancy.Tenant", null) + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tenant_frontend_configs_tenants_tenant_id"); + }); + modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => { b.HasOne("Tiku.Domain.Tenancy.Tenant", null) diff --git a/Tiku.Infrastructure/Persistence/Migrations/20260725220742_InitialSchema.cs b/Tiku.Infrastructure/Persistence/Migrations/20260727084726_InitialSchema.cs similarity index 88% rename from Tiku.Infrastructure/Persistence/Migrations/20260725220742_InitialSchema.cs rename to Tiku.Infrastructure/Persistence/Migrations/20260727084726_InitialSchema.cs index cfb7a65..5464345 100644 --- a/Tiku.Infrastructure/Persistence/Migrations/20260725220742_InitialSchema.cs +++ b/Tiku.Infrastructure/Persistence/Migrations/20260727084726_InitialSchema.cs @@ -747,6 +747,41 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "point_activity_tasks", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + task_key = table.Column(type: "citext", maxLength: 100, nullable: false), + title = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), + task_type = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + points = table.Column(type: "integer", nullable: false), + max_claims_per_user = table.Column(type: "integer", nullable: false), + starts_at = table.Column(type: "timestamp with time zone", nullable: true), + ends_at = table.Column(type: "timestamp with time zone", nullable: true), + sort_order = table.Column(type: "integer", nullable: false), + rules = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + tenant_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_point_activity_tasks", x => x.id); + table.UniqueConstraint("ak_point_activity_tasks_tenant_id_id", x => new { x.tenant_id, x.id }); + table.CheckConstraint("ck_point_activity_tasks_max_claims", "max_claims_per_user > 0"); + table.CheckConstraint("ck_point_activity_tasks_points", "points > 0"); + table.ForeignKey( + name: "fk_point_activity_tasks_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "practice_daily_usage", columns: table => new @@ -1014,6 +1049,44 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "taxonomy_nodes", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + parent_owner_tenant_id = table.Column(type: "uuid", nullable: true), + parent_id = table.Column(type: "uuid", nullable: true), + node_type = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + code = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + name = table.Column(type: "character varying(300)", maxLength: 300, nullable: false), + path = table.Column(type: "ltree", nullable: true), + depth = table.Column(type: "integer", nullable: false), + sort_order = table.Column(type: "integer", nullable: false), + is_active = table.Column(type: "boolean", nullable: false), + metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + tenant_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_taxonomy_nodes", x => x.id); + table.UniqueConstraint("ak_taxonomy_nodes_tenant_id_id", x => new { x.tenant_id, x.id }); + table.CheckConstraint("ck_taxonomy_nodes_parent_pair", "(parent_owner_tenant_id is null) = (parent_id is null)"); + table.ForeignKey( + name: "fk_taxonomy_nodes_taxonomy_nodes_parent_owner_tenant_id_parent~", + columns: x => new { x.parent_owner_tenant_id, x.parent_id }, + principalTable: "taxonomy_nodes", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_taxonomy_nodes_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "tenant_auth_providers", columns: table => new @@ -1143,6 +1216,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations is_primary = table.Column(type: "boolean", nullable: false), verification_token = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), verified_at = table.Column(type: "timestamp with time zone", nullable: true), + last_checked_at = table.Column(type: "timestamp with time zone", nullable: true), + dns_verified_at = table.Column(type: "timestamp with time zone", nullable: true), + tls_ready_at = table.Column(type: "timestamp with time zone", nullable: true), + last_failure_reason = table.Column(type: "character varying(2000)", maxLength: 2000, nullable: true), tenant_id = table.Column(type: "uuid", nullable: false), created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") @@ -1159,6 +1236,42 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "tenant_frontend_configs", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + schema_version = table.Column(type: "integer", nullable: false), + config_version = table.Column(type: "integer", nullable: false), + published_branding = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + published_theme = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + published_features = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + published_navigation = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"), + published_home_modules = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"), + draft_branding = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + draft_theme = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + draft_features = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + draft_navigation = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"), + draft_home_modules = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"), + published_at = table.Column(type: "timestamp with time zone", nullable: true), + tenant_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_tenant_frontend_configs", x => x.id); + table.UniqueConstraint("ak_tenant_frontend_configs_tenant_id_id", x => new { x.tenant_id, x.id }); + table.CheckConstraint("ck_tenant_frontend_configs_config_version", "config_version > 0"); + table.CheckConstraint("ck_tenant_frontend_configs_schema_version", "schema_version > 0"); + table.ForeignKey( + name: "fk_tenant_frontend_configs_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "tenant_invoices", columns: table => new @@ -1279,6 +1392,39 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.SetNull); }); + migrationBuilder.CreateTable( + name: "tenant_secrets", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + purpose = table.Column(type: "character varying(80)", maxLength: 80, nullable: false), + provider = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + secret_key = table.Column(type: "character varying(120)", maxLength: 120, nullable: false), + secret_ref = table.Column(type: "character varying(300)", maxLength: 300, nullable: false), + status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + encryption_key_id = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + encrypted_payload = table.Column(type: "bytea", nullable: false), + encryption_nonce = table.Column(type: "bytea", nullable: false), + encryption_tag = table.Column(type: "bytea", nullable: false), + rotated_at = table.Column(type: "timestamp with time zone", nullable: true), + expires_at = table.Column(type: "timestamp with time zone", nullable: true), + tenant_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_tenant_secrets", x => x.id); + table.UniqueConstraint("ak_tenant_secrets_tenant_id_id", x => new { x.tenant_id, x.id }); + table.CheckConstraint("ck_tenant_secrets_encryption_envelope", "octet_length(encrypted_payload) > 0 and octet_length(encryption_nonce) = 12 and octet_length(encryption_tag) = 16 and encryption_key_id <> ''"); + table.ForeignKey( + name: "fk_tenant_secrets_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "tenant_settings", columns: table => new @@ -1582,6 +1728,45 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "commission_settlement_export_events", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + tenant_id = table.Column(type: "uuid", nullable: false), + settlement_id = table.Column(type: "uuid", nullable: false), + exported_by = table.Column(type: "uuid", nullable: true), + export_format = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), + filename = table.Column(type: "character varying(500)", maxLength: 500, nullable: false), + row_count = table.Column(type: "integer", nullable: false), + content_sha256 = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), + metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_commission_settlement_export_events", x => x.id); + table.CheckConstraint("ck_commission_export_events_rows", "row_count >= 0"); + table.ForeignKey( + name: "fk_commission_settlement_export_events_commission_settlements_~", + columns: x => new { x.tenant_id, x.settlement_id }, + principalTable: "commission_settlements", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_commission_settlement_export_events_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_commission_settlement_export_events_users_exported_by", + column: x => x.exported_by, + principalTable: "users", + principalColumn: "id", + onDelete: ReferentialAction.SetNull); + }); + migrationBuilder.CreateTable( name: "commission_settlement_items", columns: table => new @@ -1761,6 +1946,49 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.SetNull); }); + migrationBuilder.CreateTable( + name: "point_activity_claims", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + task_id = table.Column(type: "uuid", nullable: false), + user_id = table.Column(type: "uuid", nullable: false), + task_key = table.Column(type: "citext", maxLength: 100, nullable: false), + points = table.Column(type: "integer", nullable: false), + status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + source_type = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + source_id = table.Column(type: "uuid", nullable: true), + claimed_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + tenant_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_point_activity_claims", x => x.id); + table.UniqueConstraint("ak_point_activity_claims_tenant_id_id", x => new { x.tenant_id, x.id }); + table.CheckConstraint("ck_point_activity_claims_points", "points > 0"); + table.ForeignKey( + name: "fk_point_activity_claims_point_activity_tasks_tenant_id_task_id", + columns: x => new { x.tenant_id, x.task_id }, + principalTable: "point_activity_tasks", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_point_activity_claims_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_point_activity_claims_users_user_id", + column: x => x.user_id, + principalTable: "users", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "ai_recommendation_reports", columns: table => new @@ -1967,6 +2195,50 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "point_exchange_items", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + region_id = table.Column(type: "uuid", nullable: true), + item_key = table.Column(type: "citext", maxLength: 100, nullable: false), + name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), + item_type = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + points_cost = table.Column(type: "integer", nullable: false), + stock = table.Column(type: "integer", nullable: true), + days = table.Column(type: "integer", nullable: true), + sort_order = table.Column(type: "integer", nullable: false), + starts_at = table.Column(type: "timestamp with time zone", nullable: true), + ends_at = table.Column(type: "timestamp with time zone", nullable: true), + fulfillment_payload = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + tenant_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_point_exchange_items", x => x.id); + table.UniqueConstraint("ak_point_exchange_items_tenant_id_id", x => new { x.tenant_id, x.id }); + table.CheckConstraint("ck_point_exchange_items_days", "days is null or days >= 0"); + table.CheckConstraint("ck_point_exchange_items_points_cost", "points_cost > 0"); + table.CheckConstraint("ck_point_exchange_items_stock", "stock is null or stock >= 0"); + table.ForeignKey( + name: "fk_point_exchange_items_regions_tenant_id_region_id", + columns: x => new { x.tenant_id, x.region_id }, + principalTable: "regions", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_point_exchange_items_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "products", columns: table => new @@ -1983,6 +2255,7 @@ namespace Tiku.Infrastructure.Persistence.Migrations cover = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), preview_iframe = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), detail_images = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"), + is_active = table.Column(type: "boolean", nullable: false, defaultValue: true), tenant_id = table.Column(type: "uuid", nullable: false), created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") @@ -2012,7 +2285,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), region_id = table.Column(type: "uuid", nullable: true), name = table.Column(type: "character varying(300)", maxLength: 300, nullable: false), - source_scope = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), tenant_id = table.Column(type: "uuid", nullable: false), @@ -2115,6 +2387,47 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "scoreline_fields", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + region_id = table.Column(type: "uuid", nullable: true), + legacy_id = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + field_key = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + field_name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + field_type = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + unit = table.Column(type: "character varying(32)", maxLength: 32, nullable: true), + is_filter = table.Column(type: "boolean", nullable: false), + is_required = table.Column(type: "boolean", nullable: false), + is_visible = table.Column(type: "boolean", nullable: false), + is_trend = table.Column(type: "boolean", nullable: false), + options = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"), + placeholder = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), + sort_order = table.Column(type: "integer", nullable: false), + tenant_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_scoreline_fields", x => x.id); + table.UniqueConstraint("ak_scoreline_fields_tenant_id_id", x => new { x.tenant_id, x.id }); + table.ForeignKey( + name: "fk_scoreline_fields_regions_tenant_id_region_id", + columns: x => new { x.tenant_id, x.region_id }, + principalTable: "regions", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_scoreline_fields_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "svip_plans", columns: table => new @@ -2447,46 +2760,49 @@ namespace Tiku.Infrastructure.Persistence.Migrations }); migrationBuilder.CreateTable( - name: "question_bank_grants", + name: "point_exchange_orders", columns: table => new { id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - source_question_bank_id = table.Column(type: "uuid", nullable: false), - created_by = table.Column(type: "uuid", nullable: true), - updated_by = table.Column(type: "uuid", nullable: true), - grant_scope = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - allowed_plan_codes = table.Column(type: "text[]", nullable: false, defaultValueSql: "'{}'::text[]"), - allowed_tenant_ids = table.Column(type: "uuid[]", nullable: false, defaultValueSql: "'{}'::uuid[]"), - allowed_region_ids = table.Column(type: "uuid[]", nullable: false, defaultValueSql: "'{}'::uuid[]"), - allowed_subject_ids = table.Column(type: "uuid[]", nullable: false, defaultValueSql: "'{}'::uuid[]"), + item_id = table.Column(type: "uuid", nullable: false), + user_id = table.Column(type: "uuid", nullable: false), + order_no = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + item_name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + item_type = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - starts_at = table.Column(type: "timestamp with time zone", nullable: true), - expires_at = table.Column(type: "timestamp with time zone", nullable: true), + points_cost = table.Column(type: "integer", nullable: false), + ordered_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + completed_at = table.Column(type: "timestamp with time zone", nullable: true), + cancelled_at = table.Column(type: "timestamp with time zone", nullable: true), + fulfillment_snapshot = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + tenant_id = table.Column(type: "uuid", nullable: false), created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") }, constraints: table => { - table.PrimaryKey("pk_question_bank_grants", x => x.id); + table.PrimaryKey("pk_point_exchange_orders", x => x.id); + table.UniqueConstraint("ak_point_exchange_orders_tenant_id_id", x => new { x.tenant_id, x.id }); + table.CheckConstraint("ck_point_exchange_orders_points_cost", "points_cost > 0"); table.ForeignKey( - name: "fk_question_bank_grants_question_banks_source_question_bank_id", - column: x => x.source_question_bank_id, - principalTable: "question_banks", + name: "fk_point_exchange_orders_point_exchange_items_tenant_id_item_id", + columns: x => new { x.tenant_id, x.item_id }, + principalTable: "point_exchange_items", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_point_exchange_orders_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", principalColumn: "id", onDelete: ReferentialAction.Cascade); table.ForeignKey( - name: "fk_question_bank_grants_users_created_by", - column: x => x.created_by, + name: "fk_point_exchange_orders_users_user_id", + column: x => x.user_id, principalTable: "users", principalColumn: "id", - onDelete: ReferentialAction.SetNull); - table.ForeignKey( - name: "fk_question_bank_grants_users_updated_by", - column: x => x.updated_by, - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.SetNull); + onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( @@ -2543,6 +2859,54 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.SetNull); }); + migrationBuilder.CreateTable( + name: "tenant_question_bank_preferences", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + question_bank_owner_tenant_id = table.Column(type: "uuid", nullable: false), + question_bank_id = table.Column(type: "uuid", nullable: false), + is_visible = table.Column(type: "boolean", nullable: false), + alias = table.Column(type: "character varying(300)", maxLength: 300, nullable: true), + sort_order = table.Column(type: "integer", nullable: false), + navigation_location = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + created_by = table.Column(type: "uuid", nullable: true), + updated_by = table.Column(type: "uuid", nullable: true), + metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + tenant_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_tenant_question_bank_preferences", x => x.id); + table.UniqueConstraint("ak_tenant_question_bank_preferences_tenant_id_id", x => new { x.tenant_id, x.id }); + table.ForeignKey( + name: "fk_tenant_question_bank_preferences_question_banks_question_ba~", + columns: x => new { x.question_bank_owner_tenant_id, x.question_bank_id }, + principalTable: "question_banks", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_tenant_question_bank_preferences_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_tenant_question_bank_preferences_users_created_by", + column: x => x.created_by, + principalTable: "users", + principalColumn: "id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "fk_tenant_question_bank_preferences_users_updated_by", + column: x => x.updated_by, + principalTable: "users", + principalColumn: "id", + onDelete: ReferentialAction.SetNull); + }); + migrationBuilder.CreateTable( name: "module_nodes", columns: table => new @@ -3284,6 +3648,53 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "scoreline_records", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + region_id = table.Column(type: "uuid", nullable: true), + school_id = table.Column(type: "uuid", nullable: true), + major_id = table.Column(type: "uuid", nullable: true), + legacy_id = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + year = table.Column(type: "integer", nullable: false), + school_name = table.Column(type: "character varying(300)", maxLength: 300, nullable: true), + major_name = table.Column(type: "character varying(300)", maxLength: 300, nullable: true), + field_values = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + tenant_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_scoreline_records", x => x.id); + table.UniqueConstraint("ak_scoreline_records_tenant_id_id", x => new { x.tenant_id, x.id }); + table.ForeignKey( + name: "fk_scoreline_records_majors_tenant_id_major_id", + columns: x => new { x.tenant_id, x.major_id }, + principalTable: "majors", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_scoreline_records_regions_tenant_id_region_id", + columns: x => new { x.tenant_id, x.region_id }, + principalTable: "regions", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_scoreline_records_schools_tenant_id_school_id", + columns: x => new { x.tenant_id, x.school_id }, + principalTable: "schools", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_scoreline_records_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "student_profiles", columns: table => new @@ -3291,6 +3702,7 @@ namespace Tiku.Infrastructure.Persistence.Migrations id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), user_id = table.Column(type: "uuid", nullable: false), legacy_user_id = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), + avatar_preset = table.Column(type: "character varying(32)", maxLength: 32, nullable: false, defaultValue: "male"), region_id = table.Column(type: "uuid", nullable: true), selected_school_id = table.Column(type: "uuid", nullable: true), selected_major_id = table.Column(type: "uuid", nullable: true), @@ -4409,6 +4821,68 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.SetNull); }); + migrationBuilder.CreateTable( + name: "commission_settlement_proofs", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + settlement_id = table.Column(type: "uuid", nullable: false), + asset_id = table.Column(type: "uuid", nullable: true), + submitted_by = table.Column(type: "uuid", nullable: true), + reviewed_by = table.Column(type: "uuid", nullable: true), + proof_type = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + title = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + description = table.Column(type: "text", nullable: true), + external_url = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + amount_cents = table.Column(type: "integer", nullable: true), + payment_method = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + payment_account = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + paid_at = table.Column(type: "timestamp with time zone", nullable: true), + reviewed_at = table.Column(type: "timestamp with time zone", nullable: true), + review_note = table.Column(type: "text", nullable: true), + metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), + tenant_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_commission_settlement_proofs", x => x.id); + table.UniqueConstraint("ak_commission_settlement_proofs_tenant_id_id", x => new { x.tenant_id, x.id }); + table.CheckConstraint("ck_commission_settlement_proofs_amount", "amount_cents is null or amount_cents >= 0"); + table.ForeignKey( + name: "fk_commission_settlement_proofs_commission_settlements_tenant_~", + columns: x => new { x.tenant_id, x.settlement_id }, + principalTable: "commission_settlements", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_commission_settlement_proofs_content_assets_tenant_id_asset~", + columns: x => new { x.tenant_id, x.asset_id }, + principalTable: "content_assets", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "fk_commission_settlement_proofs_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_commission_settlement_proofs_users_reviewed_by", + column: x => x.reviewed_by, + principalTable: "users", + principalColumn: "id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "fk_commission_settlement_proofs_users_submitted_by", + column: x => x.submitted_by, + principalTable: "users", + principalColumn: "id", + onDelete: ReferentialAction.SetNull); + }); + migrationBuilder.CreateTable( name: "content_asset_access_events", columns: table => new @@ -4606,84 +5080,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.SetNull); }); - migrationBuilder.CreateTable( - name: "tenant_question_bank_adoptions", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - source_question_bank_id = table.Column(type: "uuid", nullable: false), - grant_id = table.Column(type: "uuid", nullable: true), - target_question_bank_id = table.Column(type: "uuid", nullable: true), - target_entry_id = table.Column(type: "uuid", nullable: true), - target_collection_id = table.Column(type: "uuid", nullable: true), - created_by = table.Column(type: "uuid", nullable: true), - updated_by = table.Column(type: "uuid", nullable: true), - adoption_mode = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - sync_status = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), - source_snapshot = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - copied_question_count = table.Column(type: "integer", nullable: false), - metadata = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"), - last_synced_at = table.Column(type: "timestamp with time zone", nullable: true), - tenant_id = table.Column(type: "uuid", nullable: false), - created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") - }, - constraints: table => - { - table.PrimaryKey("pk_tenant_question_bank_adoptions", x => x.id); - table.UniqueConstraint("ak_tenant_question_bank_adoptions_tenant_id_id", x => new { x.tenant_id, x.id }); - table.CheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0"); - table.ForeignKey( - name: "fk_tenant_question_bank_adoptions_content_entries_tenant_id_ta~", - columns: x => new { x.tenant_id, x.target_entry_id }, - principalTable: "content_entries", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_tenant_question_bank_adoptions_question_bank_grants_grant_id", - column: x => x.grant_id, - principalTable: "question_bank_grants", - principalColumn: "id", - onDelete: ReferentialAction.SetNull); - table.ForeignKey( - name: "fk_tenant_question_bank_adoptions_question_banks_source_questi~", - column: x => x.source_question_bank_id, - principalTable: "question_banks", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "fk_tenant_question_bank_adoptions_question_banks_tenant_id_tar~", - columns: x => new { x.tenant_id, x.target_question_bank_id }, - principalTable: "question_banks", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_tenant_question_bank_adoptions_question_collections_tenant_~", - columns: x => new { x.tenant_id, x.target_collection_id }, - principalTable: "question_collections", - principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_tenant_question_bank_adoptions_tenants_tenant_id", - column: x => x.tenant_id, - principalTable: "tenants", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "fk_tenant_question_bank_adoptions_users_created_by", - column: x => x.created_by, - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.SetNull); - table.ForeignKey( - name: "fk_tenant_question_bank_adoptions_users_updated_by", - column: x => x.updated_by, - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.SetNull); - }); - migrationBuilder.CreateTable( name: "commerce_reconciliation_issue_events", columns: table => new @@ -4779,7 +5175,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations target_id = table.Column(type: "uuid", nullable: true), started_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), finished_at = table.Column(type: "timestamp with time zone", nullable: true), - question_ids = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"), question_count = table.Column(type: "integer", nullable: false), duration_minutes = table.Column(type: "integer", nullable: true), total_score = table.Column(type: "numeric(8,2)", precision: 8, scale: 2, nullable: true), @@ -5008,9 +5403,8 @@ namespace Tiku.Infrastructure.Persistence.Migrations { id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), user_id = table.Column(type: "uuid", nullable: false), - question_id = table.Column(type: "uuid", nullable: true), - question_version_id = table.Column(type: "uuid", nullable: true), - practice_session_id = table.Column(type: "uuid", nullable: true), + practice_session_id = table.Column(type: "uuid", nullable: false), + session_question_id = table.Column(type: "uuid", nullable: false), legacy_id = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), legacy_question_id = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), legacy_category_id = table.Column(type: "character varying(64)", maxLength: 64, nullable: true), @@ -5025,7 +5419,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations { table.PrimaryKey("pk_answer_records", x => x.id); table.UniqueConstraint("ak_answer_records_tenant_id_id", x => new { x.tenant_id, x.id }); - table.CheckConstraint("ck_answer_records_version_requires_question", "question_version_id is null or question_id is not null"); table.ForeignKey( name: "fk_answer_records_practice_sessions_tenant_id_user_id_practice~", columns: x => new { x.tenant_id, x.user_id, x.practice_session_id }, @@ -5052,13 +5445,15 @@ namespace Tiku.Infrastructure.Persistence.Migrations { tenant_id = table.Column(type: "uuid", nullable: false), user_id = table.Column(type: "uuid", nullable: false), + question_reference_id = table.Column(type: "uuid", nullable: false), + question_owner_tenant_id = table.Column(type: "uuid", nullable: false), question_id = table.Column(type: "uuid", nullable: false), source = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") }, constraints: table => { - table.PrimaryKey("pk_favorite_questions", x => new { x.tenant_id, x.user_id, x.question_id }); + table.PrimaryKey("pk_favorite_questions", x => new { x.tenant_id, x.user_id, x.question_reference_id }); table.ForeignKey( name: "fk_favorite_questions_tenants_tenant_id", column: x => x.tenant_id, @@ -5073,12 +5468,47 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "practice_session_questions", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + practice_session_id = table.Column(type: "uuid", nullable: false), + question_reference_id = table.Column(type: "uuid", nullable: false), + question_owner_tenant_id = table.Column(type: "uuid", nullable: false), + question_id = table.Column(type: "uuid", nullable: false), + question_version_id = table.Column(type: "uuid", nullable: false), + position = table.Column(type: "integer", nullable: false), + score = table.Column(type: "numeric(8,2)", precision: 8, scale: 2, nullable: true), + tenant_id = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_practice_session_questions", x => x.id); + table.UniqueConstraint("ak_practice_session_questions_tenant_id_id", x => new { x.tenant_id, x.id }); + table.UniqueConstraint("ak_practice_session_questions_tenant_id_practice_session_id_id", x => new { x.tenant_id, x.practice_session_id, x.id }); + table.ForeignKey( + name: "fk_practice_session_questions_practice_sessions_tenant_id_prac~", + columns: x => new { x.tenant_id, x.practice_session_id }, + principalTable: "practice_sessions", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_practice_session_questions_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "question_collection_items", columns: table => new { id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), collection_id = table.Column(type: "uuid", nullable: false), + question_reference_id = table.Column(type: "uuid", nullable: false), + question_owner_tenant_id = table.Column(type: "uuid", nullable: false), question_id = table.Column(type: "uuid", nullable: false), section_key = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), sort_order = table.Column(type: "integer", nullable: false), @@ -5107,6 +5537,30 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "question_taxonomy_assignments", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + tenant_id = table.Column(type: "uuid", nullable: false), + question_id = table.Column(type: "uuid", nullable: false), + taxonomy_owner_tenant_id = table.Column(type: "uuid", nullable: false), + taxonomy_node_id = table.Column(type: "uuid", nullable: false), + is_primary = table.Column(type: "boolean", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("pk_question_taxonomy_assignments", x => x.id); + table.UniqueConstraint("ak_question_taxonomy_assignments_tenant_id_id", x => new { x.tenant_id, x.id }); + table.ForeignKey( + name: "fk_question_taxonomy_assignments_taxonomy_nodes_taxonomy_owner~", + columns: x => new { x.taxonomy_owner_tenant_id, x.taxonomy_node_id }, + principalTable: "taxonomy_nodes", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + }); + migrationBuilder.CreateTable( name: "question_versions", columns: table => new @@ -5326,37 +5780,41 @@ namespace Tiku.Infrastructure.Persistence.Migrations }); migrationBuilder.CreateTable( - name: "wrong_questions", + name: "tenant_question_references", columns: table => new { - tenant_id = table.Column(type: "uuid", nullable: false), - user_id = table.Column(type: "uuid", nullable: false), + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + question_owner_tenant_id = table.Column(type: "uuid", nullable: false), question_id = table.Column(type: "uuid", nullable: false), - wrong_count = table.Column(type: "integer", nullable: false, defaultValue: 1), - last_wrong_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), - resolved_at = table.Column(type: "timestamp with time zone", nullable: true) + source = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + created_by = table.Column(type: "uuid", nullable: true), + tenant_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") }, constraints: table => { - table.PrimaryKey("pk_wrong_questions", x => new { x.tenant_id, x.user_id, x.question_id }); + table.PrimaryKey("pk_tenant_question_references", x => x.id); + table.UniqueConstraint("ak_tenant_question_references_tenant_id_id", x => new { x.tenant_id, x.id }); + table.UniqueConstraint("ak_tenant_question_references_tenant_id_question_owner_tenant_~", x => new { x.tenant_id, x.question_owner_tenant_id, x.question_id }); table.ForeignKey( - name: "fk_wrong_questions_questions_tenant_id_question_id", - columns: x => new { x.tenant_id, x.question_id }, + name: "fk_tenant_question_references_questions_question_owner_tenant_~", + columns: x => new { x.question_owner_tenant_id, x.question_id }, principalTable: "questions", principalColumns: new[] { "tenant_id", "id" }, - onDelete: ReferentialAction.Cascade); + onDelete: ReferentialAction.Restrict); table.ForeignKey( - name: "fk_wrong_questions_tenants_tenant_id", + name: "fk_tenant_question_references_tenants_tenant_id", column: x => x.tenant_id, principalTable: "tenants", principalColumn: "id", onDelete: ReferentialAction.Cascade); table.ForeignKey( - name: "fk_wrong_questions_users_user_id", - column: x => x.user_id, + name: "fk_tenant_question_references_users_created_by", + column: x => x.created_by, principalTable: "users", principalColumn: "id", - onDelete: ReferentialAction.Cascade); + onDelete: ReferentialAction.SetNull); }); migrationBuilder.CreateTable( @@ -5397,6 +5855,48 @@ namespace Tiku.Infrastructure.Persistence.Migrations onDelete: ReferentialAction.SetNull); }); + migrationBuilder.CreateTable( + name: "wrong_questions", + columns: table => new + { + tenant_id = table.Column(type: "uuid", nullable: false), + user_id = table.Column(type: "uuid", nullable: false), + question_reference_id = table.Column(type: "uuid", nullable: false), + question_owner_tenant_id = table.Column(type: "uuid", nullable: false), + question_id = table.Column(type: "uuid", nullable: false), + wrong_count = table.Column(type: "integer", nullable: false, defaultValue: 1), + last_wrong_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + resolved_at = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("pk_wrong_questions", x => new { x.tenant_id, x.user_id, x.question_reference_id }); + table.ForeignKey( + name: "fk_wrong_questions_questions_question_owner_tenant_id_question~", + columns: x => new { x.question_owner_tenant_id, x.question_id }, + principalTable: "questions", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_wrong_questions_tenant_question_references_tenant_id_questi~", + columns: x => new { x.tenant_id, x.question_reference_id }, + principalTable: "tenant_question_references", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_wrong_questions_tenants_tenant_id", + column: x => x.tenant_id, + principalTable: "tenants", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_wrong_questions_users_user_id", + column: x => x.user_id, + principalTable: "users", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "referral_leads", columns: table => new @@ -5575,14 +6075,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations unique: true); migrationBuilder.CreateIndex( - name: "ix_answer_records_tenant_id_question_id", + name: "ix_answer_records_tenant_id_practice_session_id_session_questi~", table: "answer_records", - columns: new[] { "tenant_id", "question_id" }); - - migrationBuilder.CreateIndex( - name: "ix_answer_records_tenant_id_question_id_question_version_id", - table: "answer_records", - columns: new[] { "tenant_id", "question_id", "question_version_id" }); + columns: new[] { "tenant_id", "practice_session_id", "session_question_id" }); migrationBuilder.CreateIndex( name: "ix_answer_records_tenant_id_user_id_practice_session_id", @@ -5780,17 +6275,17 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "commerce_reconciliation_issues", columns: new[] { "tenant_id", "status", "severity", "created_at" }); - migrationBuilder.CreateIndex( - name: "ix_commerce_reconciliation_items_batch_id_row_no", - table: "commerce_reconciliation_items", - columns: new[] { "batch_id", "row_no" }, - unique: true); - migrationBuilder.CreateIndex( name: "ix_commerce_reconciliation_items_tenant_id_batch_id_match_stat~", table: "commerce_reconciliation_items", columns: new[] { "tenant_id", "batch_id", "match_status", "row_no" }); + migrationBuilder.CreateIndex( + name: "ix_commerce_reconciliation_items_tenant_id_batch_id_row_no", + table: "commerce_reconciliation_items", + columns: new[] { "tenant_id", "batch_id", "row_no" }, + unique: true); + migrationBuilder.CreateIndex( name: "ix_commerce_reconciliation_items_tenant_id_order_id", table: "commerce_reconciliation_items", @@ -5867,6 +6362,16 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "commerce_refund_requests", columns: new[] { "tenant_id", "status", "created_at" }); + migrationBuilder.CreateIndex( + name: "ix_commission_settlement_export_events_exported_by", + table: "commission_settlement_export_events", + column: "exported_by"); + + migrationBuilder.CreateIndex( + name: "ix_commission_settlement_export_events_tenant_id_settlement_id~", + table: "commission_settlement_export_events", + columns: new[] { "tenant_id", "settlement_id", "created_at" }); + migrationBuilder.CreateIndex( name: "ix_commission_settlement_items_referrer_user_id", table: "commission_settlement_items", @@ -5893,6 +6398,26 @@ namespace Tiku.Infrastructure.Persistence.Migrations columns: new[] { "tenant_id", "source_type", "source_id" }, unique: true); + migrationBuilder.CreateIndex( + name: "ix_commission_settlement_proofs_reviewed_by", + table: "commission_settlement_proofs", + column: "reviewed_by"); + + migrationBuilder.CreateIndex( + name: "ix_commission_settlement_proofs_submitted_by", + table: "commission_settlement_proofs", + column: "submitted_by"); + + migrationBuilder.CreateIndex( + name: "ix_commission_settlement_proofs_tenant_id_asset_id", + table: "commission_settlement_proofs", + columns: new[] { "tenant_id", "asset_id" }); + + migrationBuilder.CreateIndex( + name: "ix_commission_settlement_proofs_tenant_id_settlement_id_status~", + table: "commission_settlement_proofs", + columns: new[] { "tenant_id", "settlement_id", "status", "created_at" }); + migrationBuilder.CreateIndex( name: "ix_commission_settlements_generated_by", table: "commission_settlements", @@ -6072,9 +6597,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations columns: new[] { "tenant_id", "job_id", "severity", "row_no" }); migrationBuilder.CreateIndex( - name: "ix_content_import_items_job_id_row_no", + name: "ix_content_import_items_tenant_id_job_id_row_no", table: "content_import_items", - columns: new[] { "job_id", "row_no" }, + columns: new[] { "tenant_id", "job_id", "row_no" }, unique: true); migrationBuilder.CreateIndex( @@ -6342,9 +6867,14 @@ namespace Tiku.Infrastructure.Persistence.Migrations columns: new[] { "tenant_id", "region_id", "is_active", "sort_order" }); migrationBuilder.CreateIndex( - name: "ix_favorite_questions_tenant_id_question_id", + name: "ix_favorite_questions_question_owner_tenant_id_question_id", table: "favorite_questions", - columns: new[] { "tenant_id", "question_id" }); + columns: new[] { "question_owner_tenant_id", "question_id" }); + + migrationBuilder.CreateIndex( + name: "ix_favorite_questions_tenant_id_question_reference_id", + table: "favorite_questions", + columns: new[] { "tenant_id", "question_reference_id" }); migrationBuilder.CreateIndex( name: "ix_favorite_questions_user_id", @@ -6515,23 +7045,16 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "orders", column: "user_id"); - migrationBuilder.CreateIndex( - name: "ix_payment_events_provider_event_id", - table: "payment_events", - columns: new[] { "provider", "event_id" }, - unique: true); - migrationBuilder.CreateIndex( name: "ix_payment_events_tenant_id_payment_id", table: "payment_events", columns: new[] { "tenant_id", "payment_id" }); migrationBuilder.CreateIndex( - name: "ix_payments_provider_provider_trade_no", - table: "payments", - columns: new[] { "provider", "provider_trade_no" }, - unique: true, - filter: "provider_trade_no is not null"); + name: "ix_payment_events_tenant_id_provider_event_id", + table: "payment_events", + columns: new[] { "tenant_id", "provider", "event_id" }, + unique: true); migrationBuilder.CreateIndex( name: "ix_payments_tenant_id_legacy_id", @@ -6544,6 +7067,13 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "payments", columns: new[] { "tenant_id", "order_id", "updated_at" }); + migrationBuilder.CreateIndex( + name: "ix_payments_tenant_id_provider_provider_trade_no", + table: "payments", + columns: new[] { "tenant_id", "provider", "provider_trade_no" }, + unique: true, + filter: "provider_trade_no is not null"); + migrationBuilder.CreateIndex( name: "ix_pb_import_issues_run_id_severity_collection_name", table: "pb_import_issues", @@ -6633,10 +7163,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations columns: new[] { "enabled", "min_reminder_level", "channel_code" }); migrationBuilder.CreateIndex( - name: "ix_platform_dunning_notification_events_channel_id_reminder_id", + name: "ix_platform_dunning_notification_events_channel_id", table: "platform_dunning_notification_events", - columns: new[] { "channel_id", "reminder_id" }, - unique: true); + column: "channel_id"); migrationBuilder.CreateIndex( name: "ix_platform_dunning_notification_events_invoice_id_status_crea~", @@ -6653,6 +7182,12 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "platform_dunning_notification_events", columns: new[] { "status", "next_attempt_at", "scheduled_at", "created_at" }); + migrationBuilder.CreateIndex( + name: "ix_platform_dunning_notification_events_tenant_id_channel_id_r~", + table: "platform_dunning_notification_events", + columns: new[] { "tenant_id", "channel_id", "reminder_id" }, + unique: true); + migrationBuilder.CreateIndex( name: "ix_platform_dunning_notification_events_tenant_id_invoice_id", table: "platform_dunning_notification_events", @@ -6679,6 +7214,71 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "platform_saas_plans", columns: new[] { "status", "sort_order" }); + migrationBuilder.CreateIndex( + name: "ix_point_activity_claims_tenant_id_task_id", + table: "point_activity_claims", + columns: new[] { "tenant_id", "task_id" }); + + migrationBuilder.CreateIndex( + name: "ix_point_activity_claims_tenant_id_user_id_created_at", + table: "point_activity_claims", + columns: new[] { "tenant_id", "user_id", "created_at" }); + + migrationBuilder.CreateIndex( + name: "ix_point_activity_claims_tenant_id_user_id_task_id_source_type~", + table: "point_activity_claims", + columns: new[] { "tenant_id", "user_id", "task_id", "source_type", "source_id" }, + unique: true, + filter: "source_type is not null and source_id is not null"); + + migrationBuilder.CreateIndex( + name: "ix_point_activity_claims_user_id", + table: "point_activity_claims", + column: "user_id"); + + migrationBuilder.CreateIndex( + name: "ix_point_activity_tasks_tenant_id_status_sort_order", + table: "point_activity_tasks", + columns: new[] { "tenant_id", "status", "sort_order" }); + + migrationBuilder.CreateIndex( + name: "ix_point_activity_tasks_tenant_id_task_key", + table: "point_activity_tasks", + columns: new[] { "tenant_id", "task_key" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_point_exchange_items_tenant_id_item_key", + table: "point_exchange_items", + columns: new[] { "tenant_id", "item_key" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_point_exchange_items_tenant_id_region_id_status_sort_order", + table: "point_exchange_items", + columns: new[] { "tenant_id", "region_id", "status", "sort_order" }); + + migrationBuilder.CreateIndex( + name: "ix_point_exchange_orders_tenant_id_item_id", + table: "point_exchange_orders", + columns: new[] { "tenant_id", "item_id" }); + + migrationBuilder.CreateIndex( + name: "ix_point_exchange_orders_tenant_id_order_no", + table: "point_exchange_orders", + columns: new[] { "tenant_id", "order_no" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_point_exchange_orders_tenant_id_user_id_created_at", + table: "point_exchange_orders", + columns: new[] { "tenant_id", "user_id", "created_at" }); + + migrationBuilder.CreateIndex( + name: "ix_point_exchange_orders_user_id", + table: "point_exchange_orders", + column: "user_id"); + migrationBuilder.CreateIndex( name: "ix_practice_access_events_tenant_id_practice_session_id", table: "practice_access_events", @@ -6747,6 +7347,22 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "practice_daily_usage", column: "user_id"); + migrationBuilder.CreateIndex( + name: "ix_practice_session_questions_question_owner_tenant_id_questio~", + table: "practice_session_questions", + columns: new[] { "question_owner_tenant_id", "question_id", "question_version_id" }); + + migrationBuilder.CreateIndex( + name: "ix_practice_session_questions_tenant_id_practice_session_id_po~", + table: "practice_session_questions", + columns: new[] { "tenant_id", "practice_session_id", "position" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_practice_session_questions_tenant_id_question_owner_tenant_~", + table: "practice_session_questions", + columns: new[] { "tenant_id", "question_owner_tenant_id", "question_id" }); + migrationBuilder.CreateIndex( name: "ix_practice_session_report_sections_tenant_id_practice_session~", table: "practice_session_report_sections", @@ -6827,46 +7443,24 @@ namespace Tiku.Infrastructure.Persistence.Migrations unique: true); migrationBuilder.CreateIndex( - name: "ix_products_tenant_id_region_id_type_sort_order", + name: "ix_products_tenant_id_region_id_type_is_active_sort_order", table: "products", - columns: new[] { "tenant_id", "region_id", "type", "sort_order" }); - - migrationBuilder.CreateIndex( - name: "ix_question_bank_grants_allowed_plan_codes", - table: "question_bank_grants", - column: "allowed_plan_codes") - .Annotation("Npgsql:IndexMethod", "gin"); - - migrationBuilder.CreateIndex( - name: "ix_question_bank_grants_allowed_tenant_ids", - table: "question_bank_grants", - column: "allowed_tenant_ids") - .Annotation("Npgsql:IndexMethod", "gin"); - - migrationBuilder.CreateIndex( - name: "ix_question_bank_grants_created_by", - table: "question_bank_grants", - column: "created_by"); - - migrationBuilder.CreateIndex( - name: "ix_question_bank_grants_source_question_bank_id_status_starts_~", - table: "question_bank_grants", - columns: new[] { "source_question_bank_id", "status", "starts_at", "expires_at" }); - - migrationBuilder.CreateIndex( - name: "ix_question_bank_grants_updated_by", - table: "question_bank_grants", - column: "updated_by"); + columns: new[] { "tenant_id", "region_id", "type", "is_active", "sort_order" }); migrationBuilder.CreateIndex( name: "ix_question_banks_tenant_id_region_id", table: "question_banks", columns: new[] { "tenant_id", "region_id" }); + migrationBuilder.CreateIndex( + name: "ix_question_collection_items_question_owner_tenant_id_question~", + table: "question_collection_items", + columns: new[] { "question_owner_tenant_id", "question_id" }); + migrationBuilder.CreateIndex( name: "ix_question_collection_items_tenant_id_collection_id_question_~", table: "question_collection_items", - columns: new[] { "tenant_id", "collection_id", "question_id" }, + columns: new[] { "tenant_id", "collection_id", "question_reference_id" }, unique: true); migrationBuilder.CreateIndex( @@ -6875,9 +7469,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations columns: new[] { "tenant_id", "collection_id", "section_key", "sort_order" }); migrationBuilder.CreateIndex( - name: "ix_question_collection_items_tenant_id_question_id", + name: "ix_question_collection_items_tenant_id_question_reference_id", table: "question_collection_items", - columns: new[] { "tenant_id", "question_id" }); + columns: new[] { "tenant_id", "question_reference_id" }); migrationBuilder.CreateIndex( name: "ix_question_collections_created_by", @@ -6925,6 +7519,17 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "question_collections", column: "updated_by"); + migrationBuilder.CreateIndex( + name: "ix_question_taxonomy_assignments_taxonomy_owner_tenant_id_taxo~", + table: "question_taxonomy_assignments", + columns: new[] { "taxonomy_owner_tenant_id", "taxonomy_node_id" }); + + migrationBuilder.CreateIndex( + name: "ix_question_taxonomy_assignments_tenant_id_question_id_taxonom~", + table: "question_taxonomy_assignments", + columns: new[] { "tenant_id", "question_id", "taxonomy_owner_tenant_id", "taxonomy_node_id" }, + unique: true); + migrationBuilder.CreateIndex( name: "ix_question_type_groups_tenant_id_legacy_id", table: "question_type_groups", @@ -6942,9 +7547,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations column: "created_by"); migrationBuilder.CreateIndex( - name: "ix_question_versions_question_id_version_no", + name: "ix_question_versions_tenant_id_question_id_version_no", table: "question_versions", - columns: new[] { "question_id", "version_no" }, + columns: new[] { "tenant_id", "question_id", "version_no" }, unique: true); migrationBuilder.CreateIndex( @@ -7240,6 +7845,49 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "schools", columns: new[] { "tenant_id", "region_id" }); + migrationBuilder.CreateIndex( + name: "ix_scoreline_fields_tenant_id_legacy_id", + table: "scoreline_fields", + columns: new[] { "tenant_id", "legacy_id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_scoreline_fields_tenant_id_region_id_field_key", + table: "scoreline_fields", + columns: new[] { "tenant_id", "region_id", "field_key" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_scoreline_fields_tenant_id_region_id_is_filter_sort_order", + table: "scoreline_fields", + columns: new[] { "tenant_id", "region_id", "is_filter", "sort_order" }); + + migrationBuilder.CreateIndex( + name: "ix_scoreline_records_tenant_id_legacy_id", + table: "scoreline_records", + columns: new[] { "tenant_id", "legacy_id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_scoreline_records_tenant_id_major_id", + table: "scoreline_records", + columns: new[] { "tenant_id", "major_id" }); + + migrationBuilder.CreateIndex( + name: "ix_scoreline_records_tenant_id_region_id_school_id_major_id_ye~", + table: "scoreline_records", + columns: new[] { "tenant_id", "region_id", "school_id", "major_id", "year" }); + + migrationBuilder.CreateIndex( + name: "ix_scoreline_records_tenant_id_school_id", + table: "scoreline_records", + columns: new[] { "tenant_id", "school_id" }); + + migrationBuilder.CreateIndex( + name: "ix_scoreline_records_tenant_id_year", + table: "scoreline_records", + columns: new[] { "tenant_id", "year" }); + migrationBuilder.CreateIndex( name: "ix_sms_send_rate_limits_updated_at", table: "sms_send_rate_limits", @@ -7336,6 +7984,17 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "svip_plans", columns: new[] { "tenant_id", "region_id", "is_active", "sort_order" }); + migrationBuilder.CreateIndex( + name: "ix_taxonomy_nodes_parent_owner_tenant_id_parent_id", + table: "taxonomy_nodes", + columns: new[] { "parent_owner_tenant_id", "parent_id" }); + + migrationBuilder.CreateIndex( + name: "ix_taxonomy_nodes_tenant_id_code", + table: "taxonomy_nodes", + columns: new[] { "tenant_id", "code" }, + unique: true); + migrationBuilder.CreateIndex( name: "ix_tenant_auth_providers_tenant_id_provider", table: "tenant_auth_providers", @@ -7458,6 +8117,12 @@ namespace Tiku.Infrastructure.Persistence.Migrations unique: true, filter: "is_primary"); + migrationBuilder.CreateIndex( + name: "ix_tenant_frontend_configs_tenant_id", + table: "tenant_frontend_configs", + column: "tenant_id", + unique: true); + migrationBuilder.CreateIndex( name: "ix_tenant_invoice_items_invoice_id", table: "tenant_invoice_items", @@ -7473,12 +8138,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "tenant_invoice_payments", columns: new[] { "invoice_id", "status" }); - migrationBuilder.CreateIndex( - name: "ix_tenant_invoice_payments_payment_no", - table: "tenant_invoice_payments", - column: "payment_no", - unique: true); - migrationBuilder.CreateIndex( name: "ix_tenant_invoice_payments_received_by", table: "tenant_invoice_payments", @@ -7489,6 +8148,12 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "tenant_invoice_payments", columns: new[] { "tenant_id", "invoice_id" }); + migrationBuilder.CreateIndex( + name: "ix_tenant_invoice_payments_tenant_id_payment_no", + table: "tenant_invoice_payments", + columns: new[] { "tenant_id", "payment_no" }, + unique: true); + migrationBuilder.CreateIndex( name: "ix_tenant_invoice_reminders_created_by", table: "tenant_invoice_reminders", @@ -7515,12 +8180,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "tenant_invoices", column: "created_by"); - migrationBuilder.CreateIndex( - name: "ix_tenant_invoices_invoice_no", - table: "tenant_invoices", - column: "invoice_no", - unique: true); - migrationBuilder.CreateIndex( name: "ix_tenant_invoices_tenant_id_billing_period_start_billing_peri~", table: "tenant_invoices", @@ -7528,6 +8187,12 @@ namespace Tiku.Infrastructure.Persistence.Migrations unique: true, filter: "invoice_type = 'usage_overage' and status <> 'void' and metadata->>'source' = 'usage_overage_auto'"); + migrationBuilder.CreateIndex( + name: "ix_tenant_invoices_tenant_id_invoice_no", + table: "tenant_invoices", + columns: new[] { "tenant_id", "invoice_no" }, + unique: true); + migrationBuilder.CreateIndex( name: "ix_tenant_invoices_tenant_id_status_due_date", table: "tenant_invoices", @@ -7556,51 +8221,36 @@ namespace Tiku.Infrastructure.Persistence.Migrations unique: true); migrationBuilder.CreateIndex( - name: "ix_tenant_question_bank_adoptions_created_by", - table: "tenant_question_bank_adoptions", + name: "ix_tenant_question_bank_preferences_created_by", + table: "tenant_question_bank_preferences", column: "created_by"); migrationBuilder.CreateIndex( - name: "ix_tenant_question_bank_adoptions_grant_id", - table: "tenant_question_bank_adoptions", - column: "grant_id"); + name: "ix_tenant_question_bank_preferences_question_bank_owner_tenant~", + table: "tenant_question_bank_preferences", + columns: new[] { "question_bank_owner_tenant_id", "question_bank_id" }); migrationBuilder.CreateIndex( - name: "ix_tenant_question_bank_adoptions_source_question_bank_id", - table: "tenant_question_bank_adoptions", - column: "source_question_bank_id"); - - migrationBuilder.CreateIndex( - name: "ix_tenant_question_bank_adoptions_tenant_id_source_question_ba~", - table: "tenant_question_bank_adoptions", - columns: new[] { "tenant_id", "source_question_bank_id" }, + name: "ix_tenant_question_bank_preferences_tenant_id_question_bank_ow~", + table: "tenant_question_bank_preferences", + columns: new[] { "tenant_id", "question_bank_owner_tenant_id", "question_bank_id" }, unique: true); migrationBuilder.CreateIndex( - name: "ix_tenant_question_bank_adoptions_tenant_id_status_updated_at", - table: "tenant_question_bank_adoptions", - columns: new[] { "tenant_id", "status", "updated_at" }); - - migrationBuilder.CreateIndex( - name: "ix_tenant_question_bank_adoptions_tenant_id_target_collection_~", - table: "tenant_question_bank_adoptions", - columns: new[] { "tenant_id", "target_collection_id" }); - - migrationBuilder.CreateIndex( - name: "ix_tenant_question_bank_adoptions_tenant_id_target_entry_id", - table: "tenant_question_bank_adoptions", - columns: new[] { "tenant_id", "target_entry_id" }); - - migrationBuilder.CreateIndex( - name: "ix_tenant_question_bank_adoptions_tenant_id_target_question_ba~", - table: "tenant_question_bank_adoptions", - columns: new[] { "tenant_id", "target_question_bank_id" }); - - migrationBuilder.CreateIndex( - name: "ix_tenant_question_bank_adoptions_updated_by", - table: "tenant_question_bank_adoptions", + name: "ix_tenant_question_bank_preferences_updated_by", + table: "tenant_question_bank_preferences", column: "updated_by"); + migrationBuilder.CreateIndex( + name: "ix_tenant_question_references_created_by", + table: "tenant_question_references", + column: "created_by"); + + migrationBuilder.CreateIndex( + name: "ix_tenant_question_references_question_owner_tenant_id_questio~", + table: "tenant_question_references", + columns: new[] { "question_owner_tenant_id", "question_id" }); + migrationBuilder.CreateIndex( name: "ix_tenant_role_templates_created_by", table: "tenant_role_templates", @@ -7622,6 +8272,23 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "tenant_role_templates", column: "updated_by"); + migrationBuilder.CreateIndex( + name: "ix_tenant_secrets_tenant_id_purpose_provider_secret_key", + table: "tenant_secrets", + columns: new[] { "tenant_id", "purpose", "provider", "secret_key" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_tenant_secrets_tenant_id_purpose_provider_status", + table: "tenant_secrets", + columns: new[] { "tenant_id", "purpose", "provider", "status" }); + + migrationBuilder.CreateIndex( + name: "ix_tenant_secrets_tenant_id_secret_ref", + table: "tenant_secrets", + columns: new[] { "tenant_id", "secret_ref" }, + unique: true); + migrationBuilder.CreateIndex( name: "ix_tenant_student_followups_assigned_to_user_id", table: "tenant_student_followups", @@ -7733,6 +8400,13 @@ namespace Tiku.Infrastructure.Persistence.Migrations column: "legacy_id", unique: true); + migrationBuilder.CreateIndex( + name: "ix_tenants_mode", + table: "tenants", + column: "mode", + unique: true, + filter: "mode = 'platform_owned'"); + migrationBuilder.CreateIndex( name: "ix_tenants_owner_user_id", table: "tenants", @@ -7946,9 +8620,14 @@ namespace Tiku.Infrastructure.Persistence.Migrations columns: new[] { "tenant_id", "unit_id", "is_active", "sort_order", "created_at" }); migrationBuilder.CreateIndex( - name: "ix_wrong_questions_tenant_id_question_id", + name: "ix_wrong_questions_question_owner_tenant_id_question_id", table: "wrong_questions", - columns: new[] { "tenant_id", "question_id" }); + columns: new[] { "question_owner_tenant_id", "question_id" }); + + migrationBuilder.CreateIndex( + name: "ix_wrong_questions_tenant_id_question_reference_id", + table: "wrong_questions", + columns: new[] { "tenant_id", "question_reference_id" }); migrationBuilder.CreateIndex( name: "ix_wrong_questions_user_id", @@ -7956,32 +8635,64 @@ namespace Tiku.Infrastructure.Persistence.Migrations column: "user_id"); migrationBuilder.AddForeignKey( - name: "fk_answer_records_question_versions_tenant_id_question_id_ques~", + name: "fk_answer_records_practice_session_questions_tenant_id_practic~", table: "answer_records", - columns: new[] { "tenant_id", "question_id", "question_version_id" }, + columns: new[] { "tenant_id", "practice_session_id", "session_question_id" }, + principalTable: "practice_session_questions", + principalColumns: new[] { "tenant_id", "practice_session_id", "id" }, + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "fk_favorite_questions_questions_question_owner_tenant_id_quest~", + table: "favorite_questions", + columns: new[] { "question_owner_tenant_id", "question_id" }, + principalTable: "questions", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "fk_favorite_questions_tenant_question_references_tenant_id_que~", + table: "favorite_questions", + columns: new[] { "tenant_id", "question_reference_id" }, + principalTable: "tenant_question_references", + principalColumns: new[] { "tenant_id", "id" }, + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "fk_practice_session_questions_question_versions_question_owner~", + table: "practice_session_questions", + columns: new[] { "question_owner_tenant_id", "question_id", "question_version_id" }, principalTable: "question_versions", principalColumns: new[] { "tenant_id", "question_id", "id" }, onDelete: ReferentialAction.Restrict); migrationBuilder.AddForeignKey( - name: "fk_answer_records_questions_tenant_id_question_id", - table: "answer_records", - columns: new[] { "tenant_id", "question_id" }, + name: "fk_practice_session_questions_tenant_question_references_tenan~", + table: "practice_session_questions", + columns: new[] { "tenant_id", "question_owner_tenant_id", "question_id" }, + principalTable: "tenant_question_references", + principalColumns: new[] { "tenant_id", "question_owner_tenant_id", "question_id" }, + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "fk_question_collection_items_questions_question_owner_tenant_i~", + table: "question_collection_items", + columns: new[] { "question_owner_tenant_id", "question_id" }, principalTable: "questions", principalColumns: new[] { "tenant_id", "id" }, onDelete: ReferentialAction.Restrict); migrationBuilder.AddForeignKey( - name: "fk_favorite_questions_questions_tenant_id_question_id", - table: "favorite_questions", - columns: new[] { "tenant_id", "question_id" }, - principalTable: "questions", + name: "fk_question_collection_items_tenant_question_references_tenant~", + table: "question_collection_items", + columns: new[] { "tenant_id", "question_reference_id" }, + principalTable: "tenant_question_references", principalColumns: new[] { "tenant_id", "id" }, onDelete: ReferentialAction.Cascade); migrationBuilder.AddForeignKey( - name: "fk_question_collection_items_questions_tenant_id_question_id", - table: "question_collection_items", + name: "fk_question_taxonomy_assignments_questions_tenant_id_question_~", + table: "question_taxonomy_assignments", columns: new[] { "tenant_id", "question_id" }, principalTable: "questions", principalColumns: new[] { "tenant_id", "id" }, @@ -8002,11 +8713,66 @@ namespace Tiku.Infrastructure.Persistence.Migrations principalTable: "referral_tracks", principalColumns: new[] { "tenant_id", "id" }, onDelete: ReferentialAction.SetNull); + + migrationBuilder.Sql(""" + create or replace function validate_tenant_question_reference() + returns trigger language plpgsql as $$ + declare owner_mode text; + begin + select mode into owner_mode from tenants where id = new.question_owner_tenant_id; + if new.question_owner_tenant_id = new.tenant_id then + if new.source <> 'tenant' then raise exception 'A tenant-owned question reference must use tenant source'; end if; + elsif owner_mode = 'platform_owned' then + if new.source <> 'platform' then raise exception 'A platform-owned question reference must use platform source'; end if; + else + raise exception 'A tenant cannot reference another tenant private question'; + end if; + return new; + end $$; + create trigger trg_validate_tenant_question_reference before insert or update on tenant_question_references + for each row execute function validate_tenant_question_reference(); + + create or replace function validate_taxonomy_owner_boundary() + returns trigger language plpgsql as $$ + declare parent_mode text; + begin + if new.parent_owner_tenant_id is null or new.parent_owner_tenant_id = new.tenant_id then return new; end if; + select mode into parent_mode from tenants where id = new.parent_owner_tenant_id; + if parent_mode <> 'platform_owned' then + raise exception 'A taxonomy node can only extend platform or current tenant taxonomy'; + end if; + return new; + end $$; + create trigger trg_validate_taxonomy_owner_boundary before insert or update on taxonomy_nodes + for each row execute function validate_taxonomy_owner_boundary(); + + create or replace function validate_question_bank_preference() + returns trigger language plpgsql as $$ + declare owner_mode text; + begin + select mode into owner_mode from tenants where id = new.question_bank_owner_tenant_id; + if owner_mode <> 'platform_owned' then + raise exception 'Question bank preferences can only target platform-owned banks'; + end if; + return new; + end $$; + create trigger trg_validate_question_bank_preference before insert or update on tenant_question_bank_preferences + for each row execute function validate_question_bank_preference(); + """); } /// protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.Sql(""" + drop trigger if exists trg_validate_question_bank_preference on tenant_question_bank_preferences; + drop function if exists validate_question_bank_preference(); + drop trigger if exists trg_validate_taxonomy_owner_boundary on taxonomy_nodes; + drop function if exists validate_taxonomy_owner_boundary(); + drop trigger if exists trg_validate_tenant_question_reference on tenant_question_references; + drop function if exists validate_tenant_question_reference(); + """); + migrationBuilder.DropForeignKey( name: "fk_content_entries_regions_tenant_id_region_id", table: "content_entries"); @@ -8148,9 +8914,65 @@ namespace Tiku.Infrastructure.Persistence.Migrations table: "referral_tracks"); migrationBuilder.DropForeignKey( - name: "fk_questions_question_versions_tenant_id_id_current_version_id", + name: "fk_categories_module_nodes_tenant_id_node_id", + table: "categories"); + + migrationBuilder.DropForeignKey( + name: "fk_questions_module_nodes_tenant_id_node_id", table: "questions"); + migrationBuilder.DropForeignKey( + name: "fk_subjects_module_nodes_tenant_id_node_id", + table: "subjects"); + + migrationBuilder.DropForeignKey( + name: "fk_categories_subjects_tenant_id_subject_id", + table: "categories"); + + migrationBuilder.DropForeignKey( + name: "fk_question_collections_subjects_tenant_id_subject_id", + table: "question_collections"); + + migrationBuilder.DropForeignKey( + name: "fk_questions_subjects_tenant_id_subject_id", + table: "questions"); + + migrationBuilder.DropForeignKey( + name: "fk_question_collections_categories_tenant_id_category_id", + table: "question_collections"); + + migrationBuilder.DropForeignKey( + name: "fk_questions_categories_tenant_id_category_id", + table: "questions"); + + migrationBuilder.DropForeignKey( + name: "fk_question_collections_content_nodes_tenant_id_node_id", + table: "question_collections"); + + migrationBuilder.DropForeignKey( + name: "fk_questions_content_nodes_tenant_id_content_node_id", + table: "questions"); + + migrationBuilder.DropForeignKey( + name: "fk_question_collections_question_banks_tenant_id_question_bank~", + table: "question_collections"); + + migrationBuilder.DropForeignKey( + name: "fk_questions_question_banks_tenant_id_question_bank_id", + table: "questions"); + + migrationBuilder.DropForeignKey( + name: "fk_question_collections_content_entries_tenant_id_entry_id", + table: "question_collections"); + + migrationBuilder.DropForeignKey( + name: "fk_questions_content_entries_tenant_id_entry_id", + table: "questions"); + + migrationBuilder.DropForeignKey( + name: "fk_question_versions_questions_tenant_id_question_id", + table: "question_versions"); + migrationBuilder.DropForeignKey( name: "fk_referral_leads_referral_tracks_tenant_id_first_track_id", table: "referral_leads"); @@ -8185,9 +9007,15 @@ namespace Tiku.Infrastructure.Persistence.Migrations migrationBuilder.DropTable( name: "commerce_refund_events"); + migrationBuilder.DropTable( + name: "commission_settlement_export_events"); + migrationBuilder.DropTable( name: "commission_settlement_items"); + migrationBuilder.DropTable( + name: "commission_settlement_proofs"); + migrationBuilder.DropTable( name: "content_asset_access_events"); @@ -8248,6 +9076,12 @@ namespace Tiku.Infrastructure.Persistence.Migrations migrationBuilder.DropTable( name: "platform_saas_plans"); + migrationBuilder.DropTable( + name: "point_activity_claims"); + + migrationBuilder.DropTable( + name: "point_exchange_orders"); + migrationBuilder.DropTable( name: "practice_access_events"); @@ -8263,6 +9097,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations migrationBuilder.DropTable( name: "question_collection_items"); + migrationBuilder.DropTable( + name: "question_taxonomy_assignments"); + migrationBuilder.DropTable( name: "question_type_groups"); @@ -8287,6 +9124,12 @@ namespace Tiku.Infrastructure.Persistence.Migrations migrationBuilder.DropTable( name: "revenue_daily_stats"); + migrationBuilder.DropTable( + name: "scoreline_fields"); + + migrationBuilder.DropTable( + name: "scoreline_records"); + migrationBuilder.DropTable( name: "sms_send_rate_limits"); @@ -8320,6 +9163,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations migrationBuilder.DropTable( name: "tenant_domains"); + migrationBuilder.DropTable( + name: "tenant_frontend_configs"); + migrationBuilder.DropTable( name: "tenant_invoice_items"); @@ -8333,7 +9179,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations name: "tenant_payment_accounts"); migrationBuilder.DropTable( - name: "tenant_question_bank_adoptions"); + name: "tenant_question_bank_preferences"); + + migrationBuilder.DropTable( + name: "tenant_secrets"); migrationBuilder.DropTable( name: "tenant_settings"); @@ -8380,6 +9229,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations migrationBuilder.DropTable( name: "coupon_redemptions"); + migrationBuilder.DropTable( + name: "practice_session_questions"); + migrationBuilder.DropTable( name: "commerce_reconciliation_issues"); @@ -8410,9 +9262,18 @@ namespace Tiku.Infrastructure.Persistence.Migrations migrationBuilder.DropTable( name: "tenant_invoice_reminders"); + migrationBuilder.DropTable( + name: "point_activity_tasks"); + + migrationBuilder.DropTable( + name: "point_exchange_items"); + migrationBuilder.DropTable( name: "practice_session_reports"); + migrationBuilder.DropTable( + name: "taxonomy_nodes"); + migrationBuilder.DropTable( name: "video_explanations"); @@ -8422,9 +9283,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations migrationBuilder.DropTable( name: "tenant_role_templates"); - migrationBuilder.DropTable( - name: "question_bank_grants"); - migrationBuilder.DropTable( name: "tenant_classes"); @@ -8440,6 +9298,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations migrationBuilder.DropTable( name: "coupons"); + migrationBuilder.DropTable( + name: "tenant_question_references"); + migrationBuilder.DropTable( name: "commerce_reconciliation_items"); @@ -8486,13 +9347,19 @@ namespace Tiku.Infrastructure.Persistence.Migrations name: "users"); migrationBuilder.DropTable( - name: "question_versions"); + name: "module_nodes"); migrationBuilder.DropTable( - name: "questions"); + name: "subjects"); migrationBuilder.DropTable( - name: "question_collections"); + name: "majors"); + + migrationBuilder.DropTable( + name: "schools"); + + migrationBuilder.DropTable( + name: "region_modules"); migrationBuilder.DropTable( name: "categories"); @@ -8503,23 +9370,17 @@ namespace Tiku.Infrastructure.Persistence.Migrations migrationBuilder.DropTable( name: "question_banks"); - migrationBuilder.DropTable( - name: "subjects"); - migrationBuilder.DropTable( name: "content_entries"); migrationBuilder.DropTable( - name: "majors"); + name: "questions"); migrationBuilder.DropTable( - name: "module_nodes"); + name: "question_collections"); migrationBuilder.DropTable( - name: "schools"); - - migrationBuilder.DropTable( - name: "region_modules"); + name: "question_versions"); migrationBuilder.DropTable( name: "referral_tracks"); diff --git a/Tiku.Infrastructure/Persistence/Migrations/TikuDbContextModelSnapshot.cs b/Tiku.Infrastructure/Persistence/Migrations/TikuDbContextModelSnapshot.cs index dd98272..63bfc12 100644 --- a/Tiku.Infrastructure/Persistence/Migrations/TikuDbContextModelSnapshot.cs +++ b/Tiku.Infrastructure/Persistence/Migrations/TikuDbContextModelSnapshot.cs @@ -286,6 +286,56 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.ToTable("module_nodes", (string)null); }); + modelBuilder.Entity("Tiku.Domain.Catalog.QuestionTaxonomyAssignment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now()"); + + b.Property("IsPrimary") + .HasColumnType("boolean") + .HasColumnName("is_primary"); + + b.Property("QuestionId") + .HasColumnType("uuid") + .HasColumnName("question_id"); + + b.Property("TaxonomyNodeId") + .HasColumnType("uuid") + .HasColumnName("taxonomy_node_id"); + + b.Property("TaxonomyOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("taxonomy_owner_tenant_id"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("tenant_id"); + + b.HasKey("Id") + .HasName("pk_question_taxonomy_assignments"); + + b.HasAlternateKey("TenantId", "Id") + .HasName("ak_question_taxonomy_assignments_tenant_id_id"); + + b.HasIndex("TaxonomyOwnerTenantId", "TaxonomyNodeId") + .HasDatabaseName("ix_question_taxonomy_assignments_taxonomy_owner_tenant_id_taxo~"); + + b.HasIndex("TenantId", "QuestionId", "TaxonomyOwnerTenantId", "TaxonomyNodeId") + .IsUnique() + .HasDatabaseName("ix_question_taxonomy_assignments_tenant_id_question_id_taxonom~"); + + b.ToTable("question_taxonomy_assignments", (string)null); + }); + modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => { b.Property("Id") @@ -861,6 +911,97 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.ToTable("subjects", (string)null); }); + modelBuilder.Entity("Tiku.Domain.Catalog.TaxonomyNode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("code"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now()"); + + b.Property("Depth") + .HasColumnType("integer") + .HasColumnName("depth"); + + b.Property("IsActive") + .HasColumnType("boolean") + .HasColumnName("is_active"); + + b.Property("Metadata") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("metadata") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("name"); + + b.Property("NodeType") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("node_type"); + + b.Property("ParentId") + .HasColumnType("uuid") + .HasColumnName("parent_id"); + + b.Property("ParentOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("parent_owner_tenant_id"); + + b.Property("Path") + .HasColumnType("ltree") + .HasColumnName("path"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasColumnName("sort_order"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("tenant_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now()"); + + b.HasKey("Id") + .HasName("pk_taxonomy_nodes"); + + b.HasAlternateKey("TenantId", "Id") + .HasName("ak_taxonomy_nodes_tenant_id_id"); + + b.HasIndex("ParentOwnerTenantId", "ParentId") + .HasDatabaseName("ix_taxonomy_nodes_parent_owner_tenant_id_parent_id"); + + b.HasIndex("TenantId", "Code") + .IsUnique() + .HasDatabaseName("ix_taxonomy_nodes_tenant_id_code"); + + b.ToTable("taxonomy_nodes", null, t => + { + t.HasCheckConstraint("ck_taxonomy_nodes_parent_pair", "(parent_owner_tenant_id is null) = (parent_id is null)"); + }); + }); + modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => { b.Property("Id") @@ -1638,10 +1779,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_commerce_reconciliation_items_tenant_id_id"); - b.HasIndex("BatchId", "RowNo") - .IsUnique() - .HasDatabaseName("ix_commerce_reconciliation_items_batch_id_row_no"); - b.HasIndex("TenantId", "OrderId") .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_id"); @@ -1651,6 +1788,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("TenantId", "RefundRequestId") .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_refund_request_id"); + b.HasIndex("TenantId", "BatchId", "RowNo") + .IsUnique() + .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_batch_id_row_no"); + b.HasIndex("TenantId", "OrderNo", "CreatedAt") .HasDatabaseName("ix_commerce_reconciliation_items_tenant_id_order_no_created_at"); @@ -2521,11 +2662,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_payments_tenant_id_id"); - b.HasIndex("Provider", "ProviderTradeNo") - .IsUnique() - .HasDatabaseName("ix_payments_provider_provider_trade_no") - .HasFilter("provider_trade_no is not null"); - b.HasIndex("TenantId", "LegacyId") .IsUnique() .HasDatabaseName("ix_payments_tenant_id_legacy_id"); @@ -2533,6 +2669,11 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("TenantId", "OrderId", "UpdatedAt") .HasDatabaseName("ix_payments_tenant_id_order_id_updated_at"); + b.HasIndex("TenantId", "Provider", "ProviderTradeNo") + .IsUnique() + .HasDatabaseName("ix_payments_tenant_id_provider_provider_trade_no") + .HasFilter("provider_trade_no is not null"); + b.ToTable("payments", null, t => { t.HasCheckConstraint("ck_payments_amount", "amount_cents >= 0"); @@ -2604,13 +2745,13 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_payment_events_tenant_id_id"); - b.HasIndex("Provider", "EventId") - .IsUnique() - .HasDatabaseName("ix_payment_events_provider_event_id"); - b.HasIndex("TenantId", "PaymentId") .HasDatabaseName("ix_payment_events_tenant_id_payment_id"); + b.HasIndex("TenantId", "Provider", "EventId") + .IsUnique() + .HasDatabaseName("ix_payment_events_tenant_id_provider_event_id"); + b.ToTable("payment_events", (string)null); }); @@ -4384,9 +4525,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_content_import_items_tenant_id_id"); - b.HasIndex("JobId", "RowNo") + b.HasIndex("TenantId", "JobId", "RowNo") .IsUnique() - .HasDatabaseName("ix_content_import_items_job_id_row_no"); + .HasDatabaseName("ix_content_import_items_tenant_id_job_id_row_no"); b.HasIndex("TenantId", "JobId", "Status", "RowNo") .HasDatabaseName("ix_content_import_items_tenant_id_job_id_status_row_no"); @@ -5276,117 +5417,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations }); }); - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.PrimitiveCollection("AllowedPlanCodes") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("text[]") - .HasColumnName("allowed_plan_codes") - .HasDefaultValueSql("'{}'::text[]"); - - b.PrimitiveCollection("AllowedRegionIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_region_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedSubjectIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_subject_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.PrimitiveCollection("AllowedTenantIds") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("uuid[]") - .HasColumnName("allowed_tenant_ids") - .HasDefaultValueSql("'{}'::uuid[]"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at") - .HasDefaultValueSql("now()"); - - b.Property("CreatedBy") - .HasColumnType("uuid") - .HasColumnName("created_by"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expires_at"); - - b.Property("GrantScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("grant_scope"); - - b.Property("Metadata") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("metadata") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("SourceQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); - - b.Property("StartsAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("starts_at"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at") - .HasDefaultValueSql("now()"); - - b.Property("UpdatedBy") - .HasColumnType("uuid") - .HasColumnName("updated_by"); - - b.HasKey("Id") - .HasName("pk_question_bank_grants"); - - b.HasIndex("AllowedPlanCodes") - .HasDatabaseName("ix_question_bank_grants_allowed_plan_codes"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedPlanCodes"), "gin"); - - b.HasIndex("AllowedTenantIds") - .HasDatabaseName("ix_question_bank_grants_allowed_tenant_ids"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("AllowedTenantIds"), "gin"); - - b.HasIndex("CreatedBy") - .HasDatabaseName("ix_question_bank_grants_created_by"); - - b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_question_bank_grants_updated_by"); - - b.HasIndex("SourceQuestionBankId", "Status", "StartsAt", "ExpiresAt") - .HasDatabaseName("ix_question_bank_grants_source_question_bank_id_status_starts_~"); - - b.ToTable("question_bank_grants", (string)null); - }); - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => { b.Property("Id") @@ -5577,6 +5607,14 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("question_id"); + b.Property("QuestionOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("question_owner_tenant_id"); + + b.Property("QuestionReferenceId") + .HasColumnType("uuid") + .HasColumnName("question_reference_id"); + b.Property("Required") .HasColumnType("boolean") .HasColumnName("required"); @@ -5611,10 +5649,13 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_question_collection_items_tenant_id_id"); - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_question_collection_items_tenant_id_question_id"); + b.HasIndex("QuestionOwnerTenantId", "QuestionId") + .HasDatabaseName("ix_question_collection_items_question_owner_tenant_id_question~"); - b.HasIndex("TenantId", "CollectionId", "QuestionId") + b.HasIndex("TenantId", "QuestionReferenceId") + .HasDatabaseName("ix_question_collection_items_tenant_id_question_reference_id"); + + b.HasIndex("TenantId", "CollectionId", "QuestionReferenceId") .IsUnique() .HasDatabaseName("ix_question_collection_items_tenant_id_collection_id_question_~"); @@ -5825,7 +5866,7 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.ToTable("subject_shares", (string)null); }); - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => + modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankPreference", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -5833,15 +5874,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("id") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("AdoptionMode") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("adoption_mode"); - - b.Property("CopiedQuestionCount") - .HasColumnType("integer") - .HasColumnName("copied_question_count"); + b.Property("Alias") + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("alias"); b.Property("CreatedAt") .ValueGeneratedOnAdd() @@ -5853,13 +5889,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("created_by"); - b.Property("GrantId") - .HasColumnType("uuid") - .HasColumnName("grant_id"); - - b.Property("LastSyncedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_synced_at"); + b.Property("IsVisible") + .HasColumnType("boolean") + .HasColumnName("is_visible"); b.Property("Metadata") .ValueGeneratedOnAdd() @@ -5867,39 +5899,22 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("metadata") .HasDefaultValueSql("'{}'::jsonb"); - b.Property("SourceQuestionBankId") + b.Property("NavigationLocation") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("navigation_location"); + + b.Property("QuestionBankId") .HasColumnType("uuid") - .HasColumnName("source_question_bank_id"); + .HasColumnName("question_bank_id"); - b.Property("SourceSnapshot") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("source_snapshot") - .HasDefaultValueSql("'{}'::jsonb"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("status"); - - b.Property("SyncStatus") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("sync_status"); - - b.Property("TargetCollectionId") + b.Property("QuestionBankOwnerTenantId") .HasColumnType("uuid") - .HasColumnName("target_collection_id"); + .HasColumnName("question_bank_owner_tenant_id"); - b.Property("TargetEntryId") - .HasColumnType("uuid") - .HasColumnName("target_entry_id"); - - b.Property("TargetQuestionBankId") - .HasColumnType("uuid") - .HasColumnName("target_question_bank_id"); + b.Property("SortOrder") + .HasColumnType("integer") + .HasColumnName("sort_order"); b.Property("TenantId") .HasColumnType("uuid") @@ -5916,43 +5931,85 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("updated_by"); b.HasKey("Id") - .HasName("pk_tenant_question_bank_adoptions"); + .HasName("pk_tenant_question_bank_preferences"); b.HasAlternateKey("TenantId", "Id") - .HasName("ak_tenant_question_bank_adoptions_tenant_id_id"); + .HasName("ak_tenant_question_bank_preferences_tenant_id_id"); b.HasIndex("CreatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_created_by"); - - b.HasIndex("GrantId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_grant_id"); - - b.HasIndex("SourceQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_source_question_bank_id"); + .HasDatabaseName("ix_tenant_question_bank_preferences_created_by"); b.HasIndex("UpdatedBy") - .HasDatabaseName("ix_tenant_question_bank_adoptions_updated_by"); + .HasDatabaseName("ix_tenant_question_bank_preferences_updated_by"); - b.HasIndex("TenantId", "SourceQuestionBankId") + b.HasIndex("QuestionBankOwnerTenantId", "QuestionBankId") + .HasDatabaseName("ix_tenant_question_bank_preferences_question_bank_owner_tenant~"); + + b.HasIndex("TenantId", "QuestionBankOwnerTenantId", "QuestionBankId") .IsUnique() - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_source_question_ba~"); + .HasDatabaseName("ix_tenant_question_bank_preferences_tenant_id_question_bank_ow~"); - b.HasIndex("TenantId", "TargetCollectionId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_collection_~"); + b.ToTable("tenant_question_bank_preferences", (string)null); + }); - b.HasIndex("TenantId", "TargetEntryId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_entry_id"); + modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionReference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); - b.HasIndex("TenantId", "TargetQuestionBankId") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_target_question_ba~"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now()"); - b.HasIndex("TenantId", "Status", "UpdatedAt") - .HasDatabaseName("ix_tenant_question_bank_adoptions_tenant_id_status_updated_at"); + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); - b.ToTable("tenant_question_bank_adoptions", null, t => - { - t.HasCheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0"); - }); + b.Property("QuestionId") + .HasColumnType("uuid") + .HasColumnName("question_id"); + + b.Property("QuestionOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("question_owner_tenant_id"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("source"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("tenant_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now()"); + + b.HasKey("Id") + .HasName("pk_tenant_question_references"); + + b.HasAlternateKey("TenantId", "Id") + .HasName("ak_tenant_question_references_tenant_id_id"); + + b.HasAlternateKey("TenantId", "QuestionOwnerTenantId", "QuestionId") + .HasName("ak_tenant_question_references_tenant_id_question_owner_tenant_~"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("ix_tenant_question_references_created_by"); + + b.HasIndex("QuestionOwnerTenantId", "QuestionId") + .HasDatabaseName("ix_tenant_question_references_question_owner_tenant_id_questio~"); + + b.ToTable("tenant_question_references", (string)null); }); modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => @@ -8325,24 +8382,20 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("character varying(64)") .HasColumnName("legacy_question_id"); - b.Property("PracticeSessionId") + b.Property("PracticeSessionId") .HasColumnType("uuid") .HasColumnName("practice_session_id"); - b.Property("QuestionId") - .HasColumnType("uuid") - .HasColumnName("question_id"); - - b.Property("QuestionVersionId") - .HasColumnType("uuid") - .HasColumnName("question_version_id"); - b.Property("SelectedOptions") .ValueGeneratedOnAdd() .HasColumnType("jsonb") .HasColumnName("selected_options") .HasDefaultValueSql("'[]'::jsonb"); + b.Property("SessionQuestionId") + .HasColumnType("uuid") + .HasColumnName("session_question_id"); + b.Property("TenantId") .HasColumnType("uuid") .HasColumnName("tenant_id"); @@ -8364,19 +8417,13 @@ namespace Tiku.Infrastructure.Persistence.Migrations .IsUnique() .HasDatabaseName("ix_answer_records_tenant_id_legacy_id"); - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id"); - - b.HasIndex("TenantId", "QuestionId", "QuestionVersionId") - .HasDatabaseName("ix_answer_records_tenant_id_question_id_question_version_id"); + b.HasIndex("TenantId", "PracticeSessionId", "SessionQuestionId") + .HasDatabaseName("ix_answer_records_tenant_id_practice_session_id_session_questi~"); b.HasIndex("TenantId", "UserId", "PracticeSessionId") .HasDatabaseName("ix_answer_records_tenant_id_user_id_practice_session_id"); - b.ToTable("answer_records", null, t => - { - t.HasCheckConstraint("ck_answer_records_version_requires_question", "question_version_id is null or question_id is not null"); - }); + b.ToTable("answer_records", (string)null); }); modelBuilder.Entity("Tiku.Domain.Learning.DashboardDailyStat", b => @@ -8566,9 +8613,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("user_id"); - b.Property("QuestionId") + b.Property("QuestionReferenceId") .HasColumnType("uuid") - .HasColumnName("question_id"); + .HasColumnName("question_reference_id"); b.Property("CreatedAt") .ValueGeneratedOnAdd() @@ -8576,20 +8623,31 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("created_at") .HasDefaultValueSql("now()"); + b.Property("QuestionId") + .HasColumnType("uuid") + .HasColumnName("question_id"); + + b.Property("QuestionOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("question_owner_tenant_id"); + b.Property("Source") .IsRequired() .HasMaxLength(50) .HasColumnType("character varying(50)") .HasColumnName("source"); - b.HasKey("TenantId", "UserId", "QuestionId") + b.HasKey("TenantId", "UserId", "QuestionReferenceId") .HasName("pk_favorite_questions"); b.HasIndex("UserId") .HasDatabaseName("ix_favorite_questions_user_id"); - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_favorite_questions_tenant_id_question_id"); + b.HasIndex("QuestionOwnerTenantId", "QuestionId") + .HasDatabaseName("ix_favorite_questions_question_owner_tenant_id_question_id"); + + b.HasIndex("TenantId", "QuestionReferenceId") + .HasDatabaseName("ix_favorite_questions_tenant_id_question_reference_id"); b.ToTable("favorite_questions", (string)null); }); @@ -8851,12 +8909,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("integer") .HasColumnName("question_count"); - b.Property("QuestionIds") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasColumnName("question_ids") - .HasDefaultValueSql("'[]'::jsonb"); - b.Property("StartedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") @@ -8918,6 +8970,69 @@ namespace Tiku.Infrastructure.Persistence.Migrations }); }); + modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Position") + .HasColumnType("integer") + .HasColumnName("position"); + + b.Property("PracticeSessionId") + .HasColumnType("uuid") + .HasColumnName("practice_session_id"); + + b.Property("QuestionId") + .HasColumnType("uuid") + .HasColumnName("question_id"); + + b.Property("QuestionOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("question_owner_tenant_id"); + + b.Property("QuestionReferenceId") + .HasColumnType("uuid") + .HasColumnName("question_reference_id"); + + b.Property("QuestionVersionId") + .HasColumnType("uuid") + .HasColumnName("question_version_id"); + + b.Property("Score") + .HasPrecision(8, 2) + .HasColumnType("numeric(8,2)") + .HasColumnName("score"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("tenant_id"); + + b.HasKey("Id") + .HasName("pk_practice_session_questions"); + + b.HasAlternateKey("TenantId", "Id") + .HasName("ak_practice_session_questions_tenant_id_id"); + + b.HasAlternateKey("TenantId", "PracticeSessionId", "Id") + .HasName("ak_practice_session_questions_tenant_id_practice_session_id_id"); + + b.HasIndex("QuestionOwnerTenantId", "QuestionId", "QuestionVersionId") + .HasDatabaseName("ix_practice_session_questions_question_owner_tenant_id_questio~"); + + b.HasIndex("TenantId", "PracticeSessionId", "Position") + .IsUnique() + .HasDatabaseName("ix_practice_session_questions_tenant_id_practice_session_id_po~"); + + b.HasIndex("TenantId", "QuestionOwnerTenantId", "QuestionId") + .HasDatabaseName("ix_practice_session_questions_tenant_id_question_owner_tenant_~"); + + b.ToTable("practice_session_questions", (string)null); + }); + modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReport", b => { b.Property("Id") @@ -9648,9 +9763,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("user_id"); - b.Property("QuestionId") + b.Property("QuestionReferenceId") .HasColumnType("uuid") - .HasColumnName("question_id"); + .HasColumnName("question_reference_id"); b.Property("LastWrongAt") .ValueGeneratedOnAdd() @@ -9658,6 +9773,14 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("last_wrong_at") .HasDefaultValueSql("now()"); + b.Property("QuestionId") + .HasColumnType("uuid") + .HasColumnName("question_id"); + + b.Property("QuestionOwnerTenantId") + .HasColumnType("uuid") + .HasColumnName("question_owner_tenant_id"); + b.Property("ResolvedAt") .HasColumnType("timestamp with time zone") .HasColumnName("resolved_at"); @@ -9668,14 +9791,17 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasDefaultValue(1) .HasColumnName("wrong_count"); - b.HasKey("TenantId", "UserId", "QuestionId") + b.HasKey("TenantId", "UserId", "QuestionReferenceId") .HasName("pk_wrong_questions"); b.HasIndex("UserId") .HasDatabaseName("ix_wrong_questions_user_id"); - b.HasIndex("TenantId", "QuestionId") - .HasDatabaseName("ix_wrong_questions_tenant_id_question_id"); + b.HasIndex("QuestionOwnerTenantId", "QuestionId") + .HasDatabaseName("ix_wrong_questions_question_owner_tenant_id_question_id"); + + b.HasIndex("TenantId", "QuestionReferenceId") + .HasDatabaseName("ix_wrong_questions_tenant_id_question_reference_id"); b.ToTable("wrong_questions", (string)null); }); @@ -11013,9 +11139,8 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_platform_dunning_notification_events_tenant_id_id"); - b.HasIndex("ChannelId", "ReminderId") - .IsUnique() - .HasDatabaseName("ix_platform_dunning_notification_events_channel_id_reminder_id"); + b.HasIndex("ChannelId") + .HasDatabaseName("ix_platform_dunning_notification_events_channel_id"); b.HasIndex("TenantId", "InvoiceId") .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_invoice_id"); @@ -11029,6 +11154,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("ReminderId", "Status", "CreatedAt") .HasDatabaseName("ix_platform_dunning_notification_events_reminder_id_status_cre~"); + b.HasIndex("TenantId", "ChannelId", "ReminderId") + .IsUnique() + .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_channel_id_r~"); + b.HasIndex("TenantId", "Status", "CreatedAt") .HasDatabaseName("ix_platform_dunning_notification_events_tenant_id_status_creat~"); @@ -11331,9 +11460,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("CreatedBy") .HasDatabaseName("ix_tenant_invoices_created_by"); - b.HasIndex("InvoiceNo") + b.HasIndex("TenantId", "InvoiceNo") .IsUnique() - .HasDatabaseName("ix_tenant_invoices_invoice_no"); + .HasDatabaseName("ix_tenant_invoices_tenant_id_invoice_no"); b.HasIndex("TenantId", "BillingPeriodStart", "BillingPeriodEnd") .IsUnique() @@ -11508,10 +11637,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasAlternateKey("TenantId", "Id") .HasName("ak_tenant_invoice_payments_tenant_id_id"); - b.HasIndex("PaymentNo") - .IsUnique() - .HasDatabaseName("ix_tenant_invoice_payments_payment_no"); - b.HasIndex("ReceivedBy") .HasDatabaseName("ix_tenant_invoice_payments_received_by"); @@ -11521,6 +11646,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("TenantId", "InvoiceId") .HasDatabaseName("ix_tenant_invoice_payments_tenant_id_invoice_id"); + b.HasIndex("TenantId", "PaymentNo") + .IsUnique() + .HasDatabaseName("ix_tenant_invoice_payments_tenant_id_payment_no"); + b.ToTable("tenant_invoice_payments", null, t => { t.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0"); @@ -11823,12 +11952,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("region_id"); - b.Property("SourceScope") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("source_scope"); - b.Property("Status") .IsRequired() .HasMaxLength(32) @@ -11947,9 +12070,9 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("CreatedBy") .HasDatabaseName("ix_question_versions_created_by"); - b.HasIndex("QuestionId", "VersionNo") + b.HasIndex("TenantId", "QuestionId", "VersionNo") .IsUnique() - .HasDatabaseName("ix_question_versions_question_id_version_no"); + .HasDatabaseName("ix_question_versions_tenant_id_question_id_version_no"); b.ToTable("question_versions", (string)null); }); @@ -12101,7 +12224,8 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("TokenHash") .IsUnique() - .HasDatabaseName("ix_auth_sessions_token_hash"); + .HasDatabaseName("ix_auth_sessions_token_hash") + .HasAnnotation("Tiku:GlobalUnique", true); b.HasIndex("UserId") .HasDatabaseName("ix_auth_sessions_user_id"); @@ -12334,6 +12458,11 @@ namespace Tiku.Infrastructure.Persistence.Migrations .IsUnique() .HasDatabaseName("ix_tenants_legacy_id"); + b.HasIndex("Mode") + .IsUnique() + .HasDatabaseName("ix_tenants_mode") + .HasFilter("mode = 'platform_owned'"); + b.HasIndex("OwnerUserId") .HasDatabaseName("ix_tenants_owner_user_id"); @@ -12691,6 +12820,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnName("created_at") .HasDefaultValueSql("now()"); + b.Property("DnsVerifiedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("dns_verified_at"); + b.Property("DomainType") .IsRequired() .HasMaxLength(32) @@ -12707,6 +12840,15 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("boolean") .HasColumnName("is_primary"); + b.Property("LastCheckedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_checked_at"); + + b.Property("LastFailureReason") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("last_failure_reason"); + b.Property("Status") .IsRequired() .HasMaxLength(32) @@ -12717,6 +12859,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasColumnType("uuid") .HasColumnName("tenant_id"); + b.Property("TlsReadyAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("tls_ready_at"); + b.Property("UpdatedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") @@ -12740,7 +12886,8 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasIndex("Host") .IsUnique() - .HasDatabaseName("ix_tenant_domains_host"); + .HasDatabaseName("ix_tenant_domains_host") + .HasAnnotation("Tiku:GlobalUnique", true); b.HasIndex("TenantId", "IsPrimary") .IsUnique() @@ -12750,6 +12897,121 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.ToTable("tenant_domains", (string)null); }); + modelBuilder.Entity("Tiku.Domain.Tenancy.TenantFrontendConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ConfigVersion") + .IsConcurrencyToken() + .HasColumnType("integer") + .HasColumnName("config_version"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now()"); + + b.Property("DraftBranding") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("draft_branding") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("DraftFeatures") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("draft_features") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("DraftHomeModules") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("draft_home_modules") + .HasDefaultValueSql("'[]'::jsonb"); + + b.Property("DraftNavigation") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("draft_navigation") + .HasDefaultValueSql("'[]'::jsonb"); + + b.Property("DraftTheme") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("draft_theme") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("PublishedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("published_at"); + + b.Property("PublishedBranding") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("published_branding") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("PublishedFeatures") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("published_features") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("PublishedHomeModules") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("published_home_modules") + .HasDefaultValueSql("'[]'::jsonb"); + + b.Property("PublishedNavigation") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("published_navigation") + .HasDefaultValueSql("'[]'::jsonb"); + + b.Property("PublishedTheme") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasColumnName("published_theme") + .HasDefaultValueSql("'{}'::jsonb"); + + b.Property("SchemaVersion") + .HasColumnType("integer") + .HasColumnName("schema_version"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("tenant_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now()"); + + b.HasKey("Id") + .HasName("pk_tenant_frontend_configs"); + + b.HasAlternateKey("TenantId", "Id") + .HasName("ak_tenant_frontend_configs_tenant_id_id"); + + b.HasIndex("TenantId") + .IsUnique() + .HasDatabaseName("ix_tenant_frontend_configs_tenant_id"); + + b.ToTable("tenant_frontend_configs", null, t => + { + t.HasCheckConstraint("ck_tenant_frontend_configs_config_version", "config_version > 0"); + + t.HasCheckConstraint("ck_tenant_frontend_configs_schema_version", "schema_version > 0"); + }); + }); + modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => { b.Property("Id") @@ -13378,6 +13640,25 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_module_nodes_regions_tenant_id_region_id"); }); + modelBuilder.Entity("Tiku.Domain.Catalog.QuestionTaxonomyAssignment", b => + { + b.HasOne("Tiku.Domain.Catalog.TaxonomyNode", null) + .WithMany() + .HasForeignKey("TaxonomyOwnerTenantId", "TaxonomyNodeId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_question_taxonomy_assignments_taxonomy_nodes_taxonomy_owner~"); + + b.HasOne("Tiku.Domain.QuestionBanks.Question", null) + .WithMany() + .HasForeignKey("TenantId", "QuestionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_question_taxonomy_assignments_questions_tenant_id_question_~"); + }); + modelBuilder.Entity("Tiku.Domain.Catalog.Region", b => { b.HasOne("Tiku.Domain.Tenancy.Tenant", null) @@ -13522,6 +13803,23 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_subjects_schools_tenant_id_school_id"); }); + modelBuilder.Entity("Tiku.Domain.Catalog.TaxonomyNode", b => + { + b.HasOne("Tiku.Domain.Tenancy.Tenant", null) + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_taxonomy_nodes_tenants_tenant_id"); + + b.HasOne("Tiku.Domain.Catalog.TaxonomyNode", null) + .WithMany() + .HasForeignKey("ParentOwnerTenantId", "ParentId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_taxonomy_nodes_taxonomy_nodes_parent_owner_tenant_id_parent~"); + }); + modelBuilder.Entity("Tiku.Domain.Commerce.ActivationCode", b => { b.HasOne("Tiku.Domain.Identity.User", null) @@ -14556,28 +14854,6 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_practice_blueprints_regions_tenant_id_region_id"); }); - modelBuilder.Entity("Tiku.Domain.Content.QuestionBankGrant", b => - { - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_created_by"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_question_bank_grants_question_banks_source_question_bank_id"); - - b.HasOne("Tiku.Domain.Identity.User", null) - .WithMany() - .HasForeignKey("UpdatedBy") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_question_bank_grants_users_updated_by"); - }); - modelBuilder.Entity("Tiku.Domain.Content.QuestionCollection", b => { b.HasOne("Tiku.Domain.Identity.User", null) @@ -14651,6 +14927,14 @@ namespace Tiku.Infrastructure.Persistence.Migrations .IsRequired() .HasConstraintName("fk_question_collection_items_tenants_tenant_id"); + b.HasOne("Tiku.Domain.QuestionBanks.Question", null) + .WithMany() + .HasForeignKey("QuestionOwnerTenantId", "QuestionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_question_collection_items_questions_question_owner_tenant_i~"); + b.HasOne("Tiku.Domain.Content.QuestionCollection", null) .WithMany() .HasForeignKey("TenantId", "CollectionId") @@ -14659,13 +14943,13 @@ namespace Tiku.Infrastructure.Persistence.Migrations .IsRequired() .HasConstraintName("fk_question_collection_items_question_collections_tenant_id_co~"); - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) + b.HasOne("Tiku.Domain.Content.TenantQuestionReference", null) .WithMany() - .HasForeignKey("TenantId", "QuestionId") + .HasForeignKey("TenantId", "QuestionReferenceId") .HasPrincipalKey("TenantId", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired() - .HasConstraintName("fk_question_collection_items_questions_tenant_id_question_id"); + .HasConstraintName("fk_question_collection_items_tenant_question_references_tenant~"); }); modelBuilder.Entity("Tiku.Domain.Content.QuestionTypeGroup", b => @@ -14735,60 +15019,58 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_subject_shares_subjects_tenant_id_target_subject_id"); }); - modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankAdoption", b => + modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionBankPreference", b => { b.HasOne("Tiku.Domain.Identity.User", null) .WithMany() .HasForeignKey("CreatedBy") .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_created_by"); - - b.HasOne("Tiku.Domain.Content.QuestionBankGrant", null) - .WithMany() - .HasForeignKey("GrantId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_bank_grants_grant_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) - .WithMany() - .HasForeignKey("SourceQuestionBankId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_source_questi~"); + .HasConstraintName("fk_tenant_question_bank_preferences_users_created_by"); b.HasOne("Tiku.Domain.Tenancy.Tenant", null) .WithMany() .HasForeignKey("TenantId") .OnDelete(DeleteBehavior.Cascade) .IsRequired() - .HasConstraintName("fk_tenant_question_bank_adoptions_tenants_tenant_id"); + .HasConstraintName("fk_tenant_question_bank_preferences_tenants_tenant_id"); b.HasOne("Tiku.Domain.Identity.User", null) .WithMany() .HasForeignKey("UpdatedBy") .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_tenant_question_bank_adoptions_users_updated_by"); - - b.HasOne("Tiku.Domain.Content.QuestionCollection", null) - .WithMany() - .HasForeignKey("TenantId", "TargetCollectionId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_collections_tenant_~"); - - b.HasOne("Tiku.Domain.Content.ContentEntry", null) - .WithMany() - .HasForeignKey("TenantId", "TargetEntryId") - .HasPrincipalKey("TenantId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_content_entries_tenant_id_ta~"); + .HasConstraintName("fk_tenant_question_bank_preferences_users_updated_by"); b.HasOne("Tiku.Domain.QuestionBanks.QuestionBank", null) .WithMany() - .HasForeignKey("TenantId", "TargetQuestionBankId") + .HasForeignKey("QuestionBankOwnerTenantId", "QuestionBankId") .HasPrincipalKey("TenantId", "Id") .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_tenant_question_bank_adoptions_question_banks_tenant_id_tar~"); + .IsRequired() + .HasConstraintName("fk_tenant_question_bank_preferences_question_banks_question_ba~"); + }); + + modelBuilder.Entity("Tiku.Domain.Content.TenantQuestionReference", b => + { + b.HasOne("Tiku.Domain.Identity.User", null) + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("fk_tenant_question_references_users_created_by"); + + b.HasOne("Tiku.Domain.Tenancy.Tenant", null) + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tenant_question_references_tenants_tenant_id"); + + b.HasOne("Tiku.Domain.QuestionBanks.Question", null) + .WithMany() + .HasForeignKey("QuestionOwnerTenantId", "QuestionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_tenant_question_references_questions_question_owner_tenant_~"); }); modelBuilder.Entity("Tiku.Domain.Content.UserWordFavorite", b => @@ -15328,25 +15610,20 @@ namespace Tiku.Infrastructure.Persistence.Migrations .IsRequired() .HasConstraintName("fk_answer_records_users_user_id"); - b.HasOne("Tiku.Domain.QuestionBanks.Question", null) + b.HasOne("Tiku.Domain.Learning.PracticeSessionQuestion", null) .WithMany() - .HasForeignKey("TenantId", "QuestionId") - .HasPrincipalKey("TenantId", "Id") + .HasForeignKey("TenantId", "PracticeSessionId", "SessionQuestionId") + .HasPrincipalKey("TenantId", "PracticeSessionId", "Id") .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_questions_tenant_id_question_id"); - - b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) - .WithMany() - .HasForeignKey("TenantId", "QuestionId", "QuestionVersionId") - .HasPrincipalKey("TenantId", "QuestionId", "Id") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_answer_records_question_versions_tenant_id_question_id_ques~"); + .IsRequired() + .HasConstraintName("fk_answer_records_practice_session_questions_tenant_id_practic~"); b.HasOne("Tiku.Domain.Learning.PracticeSession", null) .WithMany() .HasForeignKey("TenantId", "UserId", "PracticeSessionId") .HasPrincipalKey("TenantId", "UserId", "Id") .OnDelete(DeleteBehavior.Restrict) + .IsRequired() .HasConstraintName("fk_answer_records_practice_sessions_tenant_id_user_id_practice~"); }); @@ -15409,11 +15686,19 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasOne("Tiku.Domain.QuestionBanks.Question", null) .WithMany() - .HasForeignKey("TenantId", "QuestionId") + .HasForeignKey("QuestionOwnerTenantId", "QuestionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_favorite_questions_questions_question_owner_tenant_id_quest~"); + + b.HasOne("Tiku.Domain.Content.TenantQuestionReference", null) + .WithMany() + .HasForeignKey("TenantId", "QuestionReferenceId") .HasPrincipalKey("TenantId", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired() - .HasConstraintName("fk_favorite_questions_questions_tenant_id_question_id"); + .HasConstraintName("fk_favorite_questions_tenant_question_references_tenant_id_que~"); }); modelBuilder.Entity("Tiku.Domain.Learning.PracticeAccessEvent", b => @@ -15501,6 +15786,40 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_practice_sessions_content_entries_tenant_id_entry_id"); }); + modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionQuestion", b => + { + b.HasOne("Tiku.Domain.Tenancy.Tenant", null) + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_practice_session_questions_tenants_tenant_id"); + + b.HasOne("Tiku.Domain.Learning.PracticeSession", null) + .WithMany() + .HasForeignKey("TenantId", "PracticeSessionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_practice_session_questions_practice_sessions_tenant_id_prac~"); + + b.HasOne("Tiku.Domain.QuestionBanks.QuestionVersion", null) + .WithMany() + .HasForeignKey("QuestionOwnerTenantId", "QuestionId", "QuestionVersionId") + .HasPrincipalKey("TenantId", "QuestionId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_practice_session_questions_question_versions_question_owner~"); + + b.HasOne("Tiku.Domain.Content.TenantQuestionReference", null) + .WithMany() + .HasForeignKey("TenantId", "QuestionOwnerTenantId", "QuestionId") + .HasPrincipalKey("TenantId", "QuestionOwnerTenantId", "QuestionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_practice_session_questions_tenant_question_references_tenan~"); + }); + modelBuilder.Entity("Tiku.Domain.Learning.PracticeSessionReport", b => { b.HasOne("Tiku.Domain.Tenancy.Tenant", null) @@ -15687,11 +16006,19 @@ namespace Tiku.Infrastructure.Persistence.Migrations b.HasOne("Tiku.Domain.QuestionBanks.Question", null) .WithMany() - .HasForeignKey("TenantId", "QuestionId") + .HasForeignKey("QuestionOwnerTenantId", "QuestionId") + .HasPrincipalKey("TenantId", "Id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_wrong_questions_questions_question_owner_tenant_id_question~"); + + b.HasOne("Tiku.Domain.Content.TenantQuestionReference", null) + .WithMany() + .HasForeignKey("TenantId", "QuestionReferenceId") .HasPrincipalKey("TenantId", "Id") .OnDelete(DeleteBehavior.Cascade) .IsRequired() - .HasConstraintName("fk_wrong_questions_questions_tenant_id_question_id"); + .HasConstraintName("fk_wrong_questions_tenant_question_references_tenant_id_questi~"); }); modelBuilder.Entity("Tiku.Domain.Operations.Announcement", b => @@ -16295,6 +16622,16 @@ namespace Tiku.Infrastructure.Persistence.Migrations .HasConstraintName("fk_tenant_domains_tenants_tenant_id"); }); + modelBuilder.Entity("Tiku.Domain.Tenancy.TenantFrontendConfig", b => + { + b.HasOne("Tiku.Domain.Tenancy.Tenant", null) + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tenant_frontend_configs_tenants_tenant_id"); + }); + modelBuilder.Entity("Tiku.Domain.Tenancy.TenantMembership", b => { b.HasOne("Tiku.Domain.Tenancy.Tenant", null) diff --git a/Tiku.Infrastructure/Persistence/TenantIsolationSaveChangesInterceptor.cs b/Tiku.Infrastructure/Persistence/TenantIsolationSaveChangesInterceptor.cs new file mode 100644 index 0000000..beac9a8 --- /dev/null +++ b/Tiku.Infrastructure/Persistence/TenantIsolationSaveChangesInterceptor.cs @@ -0,0 +1,89 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Tiku.Application.Security; + +namespace Tiku.Infrastructure.Persistence; + +public sealed class TenantIsolationSaveChangesInterceptor(ITenantContext tenantContext) : SaveChangesInterceptor +{ + public override InterceptionResult SavingChanges( + DbContextEventData eventData, + InterceptionResult result) + { + Enforce(eventData.Context); + return result; + } + + public override ValueTask> SavingChangesAsync( + DbContextEventData eventData, + InterceptionResult result, + CancellationToken cancellationToken = default) + { + Enforce(eventData.Context); + return ValueTask.FromResult(result); + } + + private void Enforce(DbContext? dbContext) + { + if (dbContext is null) + { + return; + } + + foreach (var entry in dbContext.ChangeTracker.Entries() + .Where(entry => entry.State is EntityState.Added or EntityState.Modified or EntityState.Deleted)) + { + var tenantProperty = entry.Metadata.FindProperty("TenantId"); + if (tenantProperty?.ClrType != typeof(Guid)) + { + continue; + } + + var property = entry.Property("TenantId"); + var currentTenantId = (Guid)(property.CurrentValue ?? Guid.Empty); + var originalTenantId = (Guid)(property.OriginalValue ?? Guid.Empty); + if (entry.State == EntityState.Added) + { + if (!tenantContext.TenantId.HasValue && !tenantContext.IsSystem) + { + throw new TenantIsolationException( + entry.Metadata.ClrType, + "Cannot add tenant-owned data without a resolved tenant."); + } + + if (currentTenantId == Guid.Empty && tenantContext.TenantId.HasValue) + { + property.CurrentValue = tenantContext.TenantId.Value; + currentTenantId = tenantContext.TenantId.Value; + } + + if (!tenantContext.IsSystem && currentTenantId != tenantContext.TenantId) + { + throw new TenantIsolationException( + entry.Metadata.ClrType, + "Cannot add data for another tenant."); + } + + continue; + } + + if (property.IsModified || currentTenantId != originalTenantId) + { + throw new TenantIsolationException( + entry.Metadata.ClrType, + "Tenant ownership cannot be changed."); + } + + if (!tenantContext.IsSystem && + (!tenantContext.TenantId.HasValue || originalTenantId != tenantContext.TenantId.Value)) + { + throw new TenantIsolationException( + entry.Metadata.ClrType, + "Cannot modify or delete data owned by another tenant."); + } + } + } +} + +public sealed class TenantIsolationException(Type entityType, string message) + : InvalidOperationException($"Tenant isolation rejected {entityType.Name}: {message}"); diff --git a/Tiku.Infrastructure/Persistence/TikuDbContext.cs b/Tiku.Infrastructure/Persistence/TikuDbContext.cs index 8651a2a..c71114d 100644 --- a/Tiku.Infrastructure/Persistence/TikuDbContext.cs +++ b/Tiku.Infrastructure/Persistence/TikuDbContext.cs @@ -1,4 +1,6 @@ using Microsoft.EntityFrameworkCore; +using System.Reflection; +using Tiku.Application.Security; using Tiku.Domain.Catalog; using Tiku.Domain.Commerce; using Tiku.Domain.Common; @@ -14,8 +16,26 @@ using Tiku.Domain.Tenancy; namespace Tiku.Infrastructure.Persistence; -public sealed class TikuDbContext(DbContextOptions options) : DbContext(options) +public sealed class TikuDbContext( + DbContextOptions options, + ITenantContext tenantContext) : DbContext(options) { + public TikuDbContext(DbContextOptions options) + : this(options, CreateToolingTenantContext()) + { + } + + public Guid? CurrentTenantId => tenantContext.TenantId; + public Guid CurrentTenantIdOrEmpty => tenantContext.TenantId ?? Guid.Empty; + public bool IsTenantResolved => tenantContext.IsResolved; + public bool IsSystemScope => tenantContext.IsSystem; + + private static ITenantContext CreateToolingTenantContext() + { + var context = new TenantContext(); + context.InitializeSystem(null, "Direct DbContext construction for model tooling"); + return context; + } public DbSet Tenants => Set(); public DbSet Users => Set(); public DbSet UserIdentities => Set(); @@ -23,6 +43,7 @@ public sealed class TikuDbContext(DbContextOptions options) : DbC public DbSet TenantDomains => Set(); public DbSet TenantBrandings => Set(); public DbSet TenantSettings => Set(); + public DbSet TenantFrontendConfigs => Set(); public DbSet TenantAuthProviders => Set(); public DbSet TenantSecrets => Set(); public DbSet SmsVerificationCodes => Set(); @@ -42,6 +63,8 @@ public sealed class TikuDbContext(DbContextOptions options) : DbC public DbSet Majors => Set(); public DbSet Subjects => Set(); public DbSet Categories => Set(); + public DbSet TaxonomyNodes => Set(); + public DbSet QuestionTaxonomyAssignments => Set(); public DbSet ScorelineFields => Set(); public DbSet ScorelineRecords => Set(); public DbSet QuestionBanks => Set(); @@ -71,10 +94,11 @@ public sealed class TikuDbContext(DbContextOptions options) : DbC public DbSet AppAssets => Set(); public DbSet VideoExplanations => Set(); public DbSet QuestionVideos => Set(); - public DbSet QuestionBankGrants => Set(); - public DbSet TenantQuestionBankAdoptions => Set(); + public DbSet TenantQuestionBankPreferences => Set(); + public DbSet TenantQuestionReferences => Set(); public DbSet AiRecommendationReports => Set(); public DbSet PracticeSessions => Set(); + public DbSet PracticeSessionQuestions => Set(); public DbSet AnswerRecords => Set(); public DbSet FavoriteQuestions => Set(); public DbSet WrongQuestions => Set(); @@ -155,9 +179,97 @@ public sealed class TikuDbContext(DbContextOptions options) : DbC modelBuilder.HasPostgresExtension("citext"); modelBuilder.HasPostgresExtension("ltree"); modelBuilder.ApplyConfigurationsFromAssembly(typeof(TikuDbContext).Assembly); + ApplyTenantQueryFilters(modelBuilder); + ValidateTenantModel(modelBuilder); modelBuilder.UseSnakeCaseIdentifiers(); } + private void ApplyTenantQueryFilters(ModelBuilder modelBuilder) + { + var applyMethod = typeof(TikuDbContext) + .GetMethod(nameof(ApplyTenantQueryFilter), BindingFlags.Instance | BindingFlags.NonPublic)!; + + foreach (var entityType in modelBuilder.Model.GetEntityTypes()) + { + var tenantProperty = entityType.FindProperty("TenantId"); + if (tenantProperty?.ClrType != typeof(Guid) || entityType.BaseType is not null) + { + continue; + } + + applyMethod.MakeGenericMethod(entityType.ClrType).Invoke(this, [modelBuilder]); + } + } + + private void ApplyTenantQueryFilter(ModelBuilder modelBuilder) + where TEntity : class + { + modelBuilder.Entity().HasQueryFilter(entity => + IsSystemScope || + (IsTenantResolved && + EF.Property(entity, "TenantId") == CurrentTenantIdOrEmpty)); + } + + private static void ValidateTenantModel(ModelBuilder modelBuilder) + { + var invalidUniqueIndexes = new List(); + var invalidTenantForeignKeys = new List(); + foreach (var entityType in modelBuilder.Model.GetEntityTypes()) + { + var tenantProperty = entityType.FindProperty("TenantId"); + if (tenantProperty?.ClrType != typeof(Guid)) + { + continue; + } + + if (!typeof(ITenantOwned).IsAssignableFrom(entityType.ClrType)) + { + throw new InvalidOperationException( + $"Tenant entity '{entityType.ClrType.Name}' must implement {nameof(ITenantOwned)}."); + } + + if (!entityType.GetDeclaredQueryFilters().Any()) + { + throw new InvalidOperationException( + $"Tenant entity '{entityType.ClrType.Name}' does not have a tenant query filter."); + } + + foreach (var index in entityType.GetDeclaredIndexes().Where(index => index.IsUnique)) + { + if (index.Properties.All(property => property.Name != "TenantId") && + index.FindAnnotation("Tiku:GlobalUnique")?.Value is not true) + { + invalidUniqueIndexes.Add( + $"{entityType.ClrType.Name}({string.Join(",", index.Properties.Select(property => property.Name))})"); + } + } + + + foreach (var foreignKey in entityType.GetDeclaredForeignKeys().Where(foreignKey => + typeof(ITenantOwned).IsAssignableFrom(foreignKey.PrincipalEntityType.ClrType))) + { + if (foreignKey.PrincipalKey.Properties.All(property => property.Name != "TenantId")) + { + invalidTenantForeignKeys.Add( + $"{entityType.ClrType.Name}->{foreignKey.PrincipalEntityType.ClrType.Name}"); + } + } + } + + if (invalidUniqueIndexes.Count > 0) + { + throw new InvalidOperationException( + $"Unique indexes on tenant entities must include TenantId: {string.Join("; ", invalidUniqueIndexes)}"); + } + + + if (invalidTenantForeignKeys.Count > 0) + { + throw new InvalidOperationException( + $"Foreign keys between tenant entities must use a tenant-qualified principal key: {string.Join("; ", invalidTenantForeignKeys)}"); + } + } + public override int SaveChanges(bool acceptAllChangesOnSuccess) { UpdateTimestamps(); diff --git a/Tiku.Infrastructure/QuestionBanks/PublicQuestionAccessPolicy.cs b/Tiku.Infrastructure/QuestionBanks/PublicQuestionAccessPolicy.cs new file mode 100644 index 0000000..0357245 --- /dev/null +++ b/Tiku.Infrastructure/QuestionBanks/PublicQuestionAccessPolicy.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore; +using Tiku.Application.QuestionBanks; +using Tiku.Domain.Commerce; +using Tiku.Domain.Tenancy; +using Tiku.Infrastructure.Persistence; + +namespace Tiku.Infrastructure.QuestionBanks; + +public sealed class PublicQuestionAccessPolicy(TikuDbContext dbContext) : IPublicQuestionAccessPolicy +{ + public async Task EnsureCanStartAsync(Guid tenantId, CancellationToken cancellationToken = default) + { + var tenantIsActive = await dbContext.Tenants.AsNoTracking().AnyAsync( + tenant => tenant.Id == tenantId && tenant.Status == TenantStatus.Active, + cancellationToken); + var now = DateTimeOffset.UtcNow; + var subscriptionIsActive = await dbContext.TenantSubscriptions.AsNoTracking().AnyAsync( + subscription => + subscription.TenantId == tenantId && + (subscription.Status == TenantSubscriptionStatus.Trial || + subscription.Status == TenantSubscriptionStatus.Active) && + (!subscription.StartsAt.HasValue || subscription.StartsAt <= now) && + (!subscription.ExpiresAt.HasValue || subscription.ExpiresAt > now), + cancellationToken); + + if (!tenantIsActive || !subscriptionIsActive) + { + throw new PublicQuestionAccessDeniedException( + "public_question_subscription_required", + "An active trial or subscription is required to start public question practice."); + } + } +} diff --git a/Tiku.Infrastructure/QuestionBanks/QuestionBankQueryService.cs b/Tiku.Infrastructure/QuestionBanks/QuestionBankQueryService.cs index d38dc17..0863d60 100644 --- a/Tiku.Infrastructure/QuestionBanks/QuestionBankQueryService.cs +++ b/Tiku.Infrastructure/QuestionBanks/QuestionBankQueryService.cs @@ -1,14 +1,20 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; using Tiku.Application.Catalog; using Tiku.Application.QuestionBanks; +using Tiku.Application.Security; using Tiku.Domain.Common; using Tiku.Domain.Content; using Tiku.Domain.QuestionBanks; +using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Persistence; namespace Tiku.Infrastructure.QuestionBanks; -public sealed class QuestionBankQueryService(TikuDbContext dbContext) : IQuestionBankQueryService +public sealed class QuestionBankQueryService( + TikuDbContext dbContext, + IPublicQuestionAccessPolicy accessPolicy, + ITenantExecutionScope tenantExecutionScope) : IQuestionBankQueryService { private const int DefaultQuestionLimit = 200; private const int MaxQuestionLimit = 500; @@ -19,47 +25,84 @@ public sealed class QuestionBankQueryService(TikuDbContext dbContext) : IQuestio QuestionBankFilter filter, CancellationToken cancellationToken = default) { - var query = dbContext.QuestionBanks + var limit = ResolveLimit(filter.Limit, DefaultBankLimit, MaxBankLimit); + var tenantItems = filter.Source == QuestionSource.Platform + ? [] + : await dbContext.QuestionBanks .AsNoTracking() .Where(bank => bank.TenantId == filter.TenantId && - bank.Status == QuestionBankStatus.Active); - - if (filter.RegionId.HasValue) - { - query = query.Where(bank => bank.RegionId == filter.RegionId.Value || bank.RegionId == null); - } - - query = ApplyKeyword(query, filter.Keyword); - - var items = await query + bank.Status == QuestionBankStatus.Active && + (!filter.RegionId.HasValue || bank.RegionId == filter.RegionId.Value || bank.RegionId == null) && + (string.IsNullOrWhiteSpace(filter.Keyword) || bank.Name.Contains(filter.Keyword.Trim()))) .OrderBy(bank => bank.Name) .ThenBy(bank => bank.CreatedAt) - .Take(ResolveLimit(filter.Limit, DefaultBankLimit, MaxBankLimit)) + .Take(limit) .Select(bank => new QuestionBankCatalogItem( bank.Id, bank.RegionId, bank.Name, - bank.SourceScope, + QuestionSource.Tenant, bank.Status, bank.Metadata)) .ToArrayAsync(cancellationToken); - return new CatalogList(items); + var platformItems = filter.Source == QuestionSource.Tenant || !await CanAccessPlatformAsync(filter.TenantId, cancellationToken) + ? [] + : await tenantExecutionScope.ExecuteAsync( + filter.TenantId, + "List platform question banks for an entitled tenant", + async (provider, token) => + { + var systemDbContext = provider.GetRequiredService(); + return await systemDbContext.QuestionBanks.AsNoTracking() + .Join( + systemDbContext.Tenants.AsNoTracking().Where(tenant => tenant.Mode == TenantMode.PlatformOwned), + bank => bank.TenantId, + tenant => tenant.Id, + (bank, tenant) => bank) + .Where(bank => + bank.Status == QuestionBankStatus.Active && + (!filter.RegionId.HasValue || bank.RegionId == filter.RegionId.Value || bank.RegionId == null) && + (string.IsNullOrWhiteSpace(filter.Keyword) || bank.Name.Contains(filter.Keyword.Trim()))) + .OrderBy(bank => bank.Name) + .Take(limit) + .Select(bank => new QuestionBankCatalogItem( + bank.Id, + bank.RegionId, + bank.Name, + QuestionSource.Platform, + bank.Status, + bank.Metadata)) + .ToArrayAsync(token); + }, + cancellationToken); + + return new CatalogList(tenantItems + .Concat(platformItems) + .OrderBy(item => item.Name) + .Take(limit) + .ToArray()); } public async Task> GetQuestionsAsync( QuestionBankFilter filter, CancellationToken cancellationToken = default) { - var questions = ApplyQuestionFilters(BaseQuestionQuery(), filter) - .OrderByDescending(question => question.CreatedAt) - .Take(ResolveLimit(filter.Limit, DefaultQuestionLimit, MaxQuestionLimit)); - - var items = await ProjectQuestions(questions) - .ToArrayAsync(cancellationToken); - - return new CatalogList(items); + var limit = ResolveLimit(filter.Limit, DefaultQuestionLimit, MaxQuestionLimit); + var tenantItems = filter.Source == QuestionSource.Platform + ? [] + : await ProjectQuestions( + dbContext, + ApplyQuestionFilters(BaseQuestionQuery(), filter) + .OrderByDescending(question => question.CreatedAt) + .Take(limit), + QuestionSource.Tenant) + .ToArrayAsync(cancellationToken); + var platformItems = filter.Source == QuestionSource.Tenant || !await CanAccessPlatformAsync(filter.TenantId, cancellationToken) + ? [] + : await GetPlatformQuestionsAsync(filter, limit, cancellationToken); + return new CatalogList(tenantItems.Concat(platformItems).Take(limit).ToArray()); } public async Task GetQuestionAsync( @@ -71,13 +114,9 @@ public sealed class QuestionBankQueryService(TikuDbContext dbContext) : IQuestio throw new QuestionBankRequiredFieldException("questionId is required."); } - var questions = ApplyQuestionFilters(BaseQuestionQuery(), filter) - .Where(question => question.Id == filter.QuestionId.Value); - - var question = await ProjectQuestions(questions) - .SingleOrDefaultAsync(cancellationToken); - - return question ?? throw new QuestionBankNotFoundException("Question was not found."); + var result = await GetQuestionsAsync(filter with { Limit = 2 }, cancellationToken); + return result.Items.SingleOrDefault() + ?? throw new QuestionBankNotFoundException("Question was not found."); } public async Task> GetQuestionVersionsAsync( @@ -89,6 +128,13 @@ public sealed class QuestionBankQueryService(TikuDbContext dbContext) : IQuestio throw new QuestionBankRequiredFieldException("questionId is required."); } + if (filter.Source == QuestionSource.Platform) + { + await accessPolicy.EnsureCanStartAsync(filter.TenantId, cancellationToken); + var platformItems = await GetPlatformVersionsAsync(filter.QuestionId.Value, filter.TenantId, cancellationToken); + return new CatalogList(platformItems); + } + var questionExists = await dbContext.Questions .AsNoTracking() .AnyAsync( @@ -221,14 +267,17 @@ public sealed class QuestionBankQueryService(TikuDbContext dbContext) : IQuestio return query.Where(entity => EF.Property(entity, nameof(QuestionBank.Name)).Contains(trimmed)); } - private IQueryable ProjectQuestions(IQueryable questions) + private static IQueryable ProjectQuestions( + TikuDbContext context, + IQueryable questions, + QuestionSource source) { var emptyOptions = JsonDefaults.Array(); var emptyCorrectOptionIndices = JsonDefaults.Array(); var emptySubQuestions = JsonDefaults.Array(); return from question in questions - join version in dbContext.QuestionVersions.AsNoTracking() + join version in context.QuestionVersions.AsNoTracking() on new { question.TenantId, QuestionId = question.Id, VersionId = question.CurrentVersionId } equals new { version.TenantId, version.QuestionId, VersionId = (Guid?)version.Id } into versions @@ -264,7 +313,113 @@ public sealed class QuestionBankQueryService(TikuDbContext dbContext) : IQuestio version == null ? null : version.Explanation, version == null ? emptySubQuestions : version.SubQuestions, version == null ? null : version.CodeLang, - version == null ? null : version.CodeTemplate); + version == null ? null : version.CodeTemplate, + new QuestionLocator(source, question.Id)); + } + + private async Task CanAccessPlatformAsync(Guid tenantId, CancellationToken cancellationToken) + { + try + { + await accessPolicy.EnsureCanStartAsync(tenantId, cancellationToken); + return true; + } + catch (PublicQuestionAccessDeniedException) + { + return false; + } + } + + private Task GetPlatformQuestionsAsync( + QuestionBankFilter filter, + int limit, + CancellationToken cancellationToken) + { + return tenantExecutionScope.ExecuteAsync( + filter.TenantId, + "List platform questions for an entitled tenant", + async (provider, token) => + { + var systemDbContext = provider.GetRequiredService(); + var platformTenantId = await systemDbContext.Tenants.AsNoTracking() + .Where(tenant => tenant.Mode == TenantMode.PlatformOwned) + .Select(tenant => tenant.Id) + .SingleAsync(token); + var query = systemDbContext.Questions.AsNoTracking().Where(question => + question.TenantId == platformTenantId && + question.Status == QuestionStatus.Published && + (!filter.QuestionId.HasValue || question.Id == filter.QuestionId.Value) && + (!filter.QuestionBankId.HasValue || question.QuestionBankId == filter.QuestionBankId.Value) && + (!filter.SubjectId.HasValue || question.SubjectId == filter.SubjectId.Value) && + (!filter.CategoryId.HasValue || question.CategoryId == filter.CategoryId.Value) && + (!filter.NodeId.HasValue || question.NodeId == filter.NodeId.Value) && + (!filter.EntryId.HasValue || question.EntryId == filter.EntryId.Value) && + (!filter.ContentNodeId.HasValue || question.ContentNodeId == filter.ContentNodeId.Value) && + (filter.QuestionIds == null || filter.QuestionIds.Count == 0 || filter.QuestionIds.Contains(question.Id)) && + (string.IsNullOrWhiteSpace(filter.Type) || question.Type == filter.Type.Trim())); + if (!string.IsNullOrWhiteSpace(filter.Keyword)) + { + var keyword = filter.Keyword.Trim(); + query = query.Where(question => + question.Type.Contains(keyword) || + (question.TypeLabel != null && question.TypeLabel.Contains(keyword))); + } + + return await ProjectQuestions( + systemDbContext, + query.OrderByDescending(question => question.CreatedAt).Take(limit), + QuestionSource.Platform) + .ToArrayAsync(token); + }, + cancellationToken); + } + + private Task GetPlatformVersionsAsync( + Guid questionId, + Guid tenantId, + CancellationToken cancellationToken) + { + return tenantExecutionScope.ExecuteAsync( + tenantId, + "Read platform question versions for an entitled tenant", + async (provider, token) => + { + var systemDbContext = provider.GetRequiredService(); + var platformQuestion = await systemDbContext.Questions.AsNoTracking() + .Where(question => question.Id == questionId && question.Status == QuestionStatus.Published) + .Join( + systemDbContext.Tenants.AsNoTracking().Where(tenant => tenant.Mode == TenantMode.PlatformOwned), + question => question.TenantId, + tenant => tenant.Id, + (question, tenant) => new { question.TenantId, question.Id }) + .SingleOrDefaultAsync(token); + if (platformQuestion is null) + { + throw new QuestionBankNotFoundException("Question was not found."); + } + + return await systemDbContext.QuestionVersions.AsNoTracking() + .Where(version => + version.TenantId == platformQuestion.TenantId && + version.QuestionId == platformQuestion.Id) + .OrderByDescending(version => version.VersionNo) + .Select(version => new QuestionVersionCatalogItem( + version.Id, + version.QuestionId, + version.VersionNo, + version.Content, + version.Options, + version.CorrectOptionIndex, + version.CorrectOptionIndices, + version.AnswerText, + version.Explanation, + version.SubQuestions, + version.CodeLang, + version.CodeTemplate, + version.CreatedAt)) + .ToArrayAsync(token); + }, + cancellationToken); } private static int ResolveLimit(int? limit, int defaultLimit, int maxLimit) diff --git a/Tiku.Infrastructure/QuestionBanks/QuestionReferenceService.cs b/Tiku.Infrastructure/QuestionBanks/QuestionReferenceService.cs new file mode 100644 index 0000000..f1d9a39 --- /dev/null +++ b/Tiku.Infrastructure/QuestionBanks/QuestionReferenceService.cs @@ -0,0 +1,98 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Tiku.Application.QuestionBanks; +using Tiku.Application.Security; +using Tiku.Domain.Content; +using Tiku.Domain.QuestionBanks; +using Tiku.Domain.Tenancy; +using Tiku.Infrastructure.Persistence; + +namespace Tiku.Infrastructure.QuestionBanks; + +public sealed class QuestionReferenceService( + TikuDbContext dbContext, + IPublicQuestionAccessPolicy accessPolicy, + ITenantExecutionScope tenantExecutionScope) : IQuestionReferenceService +{ + public async Task ResolveAsync( + Guid tenantId, + Guid? userId, + QuestionLocator locator, + CancellationToken cancellationToken = default) + { + var ownerTenantId = locator.Source switch + { + QuestionSource.Tenant => await ResolveTenantQuestionAsync(tenantId, locator.QuestionId, cancellationToken), + QuestionSource.Platform => await ResolvePlatformQuestionAsync(tenantId, locator.QuestionId, cancellationToken), + _ => throw new QuestionLocatorException("question_source_invalid", "Question source is invalid.") + }; + + var existing = await dbContext.TenantQuestionReferences.SingleOrDefaultAsync( + reference => + reference.TenantId == tenantId && + reference.QuestionOwnerTenantId == ownerTenantId && + reference.QuestionId == locator.QuestionId, + cancellationToken); + if (existing is not null) + { + return existing; + } + + var reference = new TenantQuestionReference + { + TenantId = tenantId, + QuestionOwnerTenantId = ownerTenantId, + QuestionId = locator.QuestionId, + Source = locator.Source, + CreatedBy = userId + }; + dbContext.TenantQuestionReferences.Add(reference); + return reference; + } + + private async Task ResolveTenantQuestionAsync( + Guid tenantId, + Guid questionId, + CancellationToken cancellationToken) + { + var exists = await dbContext.Questions.AsNoTracking().AnyAsync( + question => + question.TenantId == tenantId && + question.Id == questionId && + question.Status == QuestionStatus.Published, + cancellationToken); + return exists + ? tenantId + : throw new QuestionLocatorException("question_not_found", "Tenant question was not found."); + } + + private async Task ResolvePlatformQuestionAsync( + Guid tenantId, + Guid questionId, + CancellationToken cancellationToken) + { + await accessPolicy.EnsureCanStartAsync(tenantId, cancellationToken); + var ownerTenantId = await tenantExecutionScope.ExecuteAsync( + tenantId, + "Resolve a platform question for an entitled tenant", + async (provider, token) => + { + var systemDbContext = provider.GetRequiredService(); + return await systemDbContext.Questions.AsNoTracking() + .Where(question => + question.Id == questionId && + question.Status == QuestionStatus.Published) + .Join( + systemDbContext.Tenants.AsNoTracking().Where(tenant => tenant.Mode == TenantMode.PlatformOwned), + question => question.TenantId, + tenant => tenant.Id, + (question, tenant) => (Guid?)tenant.Id) + .SingleOrDefaultAsync(token); + }, + cancellationToken); + + return ownerTenantId ?? throw new QuestionLocatorException( + "question_not_found", + "Platform question was not found."); + } +} diff --git a/Tiku.Infrastructure/Tenancy/TenantDirectory.cs b/Tiku.Infrastructure/Tenancy/TenantDirectory.cs new file mode 100644 index 0000000..e548481 --- /dev/null +++ b/Tiku.Infrastructure/Tenancy/TenantDirectory.cs @@ -0,0 +1,66 @@ +using Npgsql; +using Tiku.Application.Tenancy; +using Tiku.Domain.Tenancy; + +namespace Tiku.Infrastructure.Tenancy; + +public sealed class TenantDirectory(NpgsqlDataSource dataSource) : ITenantDirectory +{ + public Task FindByHostAsync( + string host, + CancellationToken cancellationToken = default) + { + const string sql = """ + select t.id, t.slug, t.name, t.status, t.mode, d.host + from tenant_domains d + join tenants t on t.id = d.tenant_id + where d.host = @lookup + and d.status = 'active' + and t.status = 'active' + limit 1 + """; + return FindAsync(sql, host, cancellationToken); + } + + public Task FindByCodeAsync( + string tenantCode, + CancellationToken cancellationToken = default) + { + const string sql = """ + select t.id, t.slug, t.name, t.status, t.mode, null::text as host + from tenants t + where t.slug = @lookup + and t.status = 'active' + limit 1 + """; + return FindAsync(sql, tenantCode, cancellationToken); + } + + private async Task FindAsync( + string sql, + string lookup, + CancellationToken cancellationToken) + { + await using var command = dataSource.CreateCommand(sql); + command.Parameters.AddWithValue("lookup", lookup); + await using var reader = await command.ExecuteReaderAsync(cancellationToken); + if (!await reader.ReadAsync(cancellationToken)) + { + return null; + } + + return new TenantDirectoryEntry( + reader.GetGuid(0), + reader.GetString(1), + reader.GetString(2), + ParseEnum(reader.GetString(3)), + ParseEnum(reader.GetString(4)), + reader.IsDBNull(5) ? null : reader.GetString(5)); + } + + private static TEnum ParseEnum(string value) + where TEnum : struct, Enum + { + return Enum.Parse(value.Replace("_", string.Empty, StringComparison.Ordinal), true); + } +} diff --git a/Tiku.Infrastructure/Tenancy/TenantDomainLifecycleService.cs b/Tiku.Infrastructure/Tenancy/TenantDomainLifecycleService.cs new file mode 100644 index 0000000..5652fbd --- /dev/null +++ b/Tiku.Infrastructure/Tenancy/TenantDomainLifecycleService.cs @@ -0,0 +1,196 @@ +using System.Net.Http.Json; +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; +using Tiku.Application.Tenancy; +using Tiku.Domain.Tenancy; +using Tiku.Infrastructure.Persistence; + +namespace Tiku.Infrastructure.Tenancy; + +public sealed class DnsDomainOwnershipVerifier( + HttpClient httpClient, + IOptions options) : IDomainOwnershipVerifier +{ + private readonly DomainLifecycleOptions options = options.Value; + + public async Task VerifyAsync( + string host, + string verificationToken, + CancellationToken cancellationToken = default) + { + if (options.AllowedCnameTargets.Length == 0 || string.IsNullOrWhiteSpace(options.DnsJsonEndpoint)) + { + return new(false, false, "DNS verification is not configured."); + } + + try + { + var cnameAnswers = await QueryAsync(host, "CNAME", cancellationToken); + var cnameMatches = cnameAnswers.Any(answer => options.AllowedCnameTargets.Any(target => + NormalizeDnsName(answer).Equals(NormalizeDnsName(target), StringComparison.OrdinalIgnoreCase))); + if (!cnameMatches) + { + return new(false, true, "CNAME does not point to an allowed gateway target."); + } + + var verificationName = $"{options.VerificationRecordPrefix.Trim().TrimEnd('.')}.{host}"; + var txtAnswers = await QueryAsync(verificationName, "TXT", cancellationToken); + var txtMatches = txtAnswers.Any(answer => + answer.Trim().Trim('"').Equals(verificationToken, StringComparison.Ordinal)); + return txtMatches + ? new(true, true, null) + : new(false, true, "TXT ownership token was not found."); + } + catch (Exception exception) when (exception is HttpRequestException or JsonException or TaskCanceledException) + { + return new(false, true, $"DNS verification failed: {exception.Message}"); + } + } + + private async Task QueryAsync(string name, string type, CancellationToken cancellationToken) + { + var endpoint = options.DnsJsonEndpoint.TrimEnd('/'); + using var request = new HttpRequestMessage( + HttpMethod.Get, + $"{endpoint}?name={Uri.EscapeDataString(name)}&type={type}"); + request.Headers.Accept.ParseAdd("application/dns-json"); + using var response = await httpClient.SendAsync(request, cancellationToken); + response.EnsureSuccessStatusCode(); + using var document = JsonDocument.Parse(await response.Content.ReadAsStringAsync(cancellationToken)); + if (!document.RootElement.TryGetProperty("Answer", out var answers) || answers.ValueKind != JsonValueKind.Array) + { + return []; + } + + return answers.EnumerateArray() + .Where(answer => answer.TryGetProperty("data", out _)) + .Select(answer => answer.GetProperty("data").GetString() ?? string.Empty) + .Where(answer => answer.Length > 0) + .ToArray(); + } + + private static string NormalizeDnsName(string value) => value.Trim().Trim('"').TrimEnd('.'); +} + +public sealed class HttpDomainGatewayProvisioner( + HttpClient httpClient, + IOptions options) : IDomainGatewayProvisioner +{ + private readonly DomainLifecycleOptions options = options.Value; + + public async Task EnsureTlsAsync( + string host, + CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(options.GatewayBaseUrl) || string.IsNullOrWhiteSpace(options.GatewayApiKey)) + { + return new(false, false, "Gateway TLS provisioning is not configured."); + } + + try + { + using var request = new HttpRequestMessage( + HttpMethod.Post, + $"{options.GatewayBaseUrl.TrimEnd('/')}/domains/ensure"); + request.Headers.Authorization = new("Bearer", options.GatewayApiKey); + request.Content = JsonContent.Create(new { host }); + using var response = await httpClient.SendAsync(request, cancellationToken); + if (!response.IsSuccessStatusCode) + { + return new(false, true, $"Gateway returned HTTP {(int)response.StatusCode}."); + } + + using var document = JsonDocument.Parse(await response.Content.ReadAsStringAsync(cancellationToken)); + var tlsReady = document.RootElement.TryGetProperty("tlsReady", out var value) && value.GetBoolean(); + return tlsReady + ? new(true, true, null) + : new(false, true, "Gateway route exists but TLS is not ready."); + } + catch (Exception exception) when (exception is HttpRequestException or JsonException or TaskCanceledException) + { + return new(false, true, $"Gateway provisioning failed: {exception.Message}"); + } + } +} + +public sealed class TenantRuntimeCacheInvalidator(IMemoryCache cache) : ITenantRuntimeCacheInvalidator +{ + public void Invalidate(Guid tenantId) => cache.Remove($"tenant-runtime:{tenantId:N}"); +} + +public sealed class TenantDomainLifecycleService( + TikuDbContext dbContext, + IDomainOwnershipVerifier ownershipVerifier, + IDomainGatewayProvisioner gatewayProvisioner, + ITenantRuntimeCacheInvalidator cacheInvalidator, + IOptions options) : ITenantDomainLifecycleService +{ + private readonly DomainLifecycleOptions options = options.Value; + + public async Task ProcessPendingAsync(CancellationToken cancellationToken = default) + { + if (!options.Enabled) + { + return 0; + } + + var domains = await dbContext.TenantDomains + .Where(domain => + domain.DomainType == TenantDomainType.Custom && + (domain.Status == TenantDomainStatus.Pending || domain.Status == TenantDomainStatus.Failed)) + .OrderBy(domain => domain.LastCheckedAt) + .Take(Math.Clamp(options.BatchSize, 1, 500)) + .ToArrayAsync(cancellationToken); + + foreach (var domain in domains) + { + await ProcessAsync(domain, cancellationToken); + } + + if (domains.Length > 0) + { + await dbContext.SaveChangesAsync(cancellationToken); + } + + return domains.Length; + } + + private async Task ProcessAsync(TenantDomain domain, CancellationToken cancellationToken) + { + domain.LastCheckedAt = DateTimeOffset.UtcNow; + if (string.IsNullOrWhiteSpace(domain.VerificationToken)) + { + Fail(domain, true, "Domain verification token is missing."); + return; + } + + var ownership = await ownershipVerifier.VerifyAsync(domain.Host, domain.VerificationToken, cancellationToken); + if (!ownership.Verified) + { + Fail(domain, ownership.Configured, ownership.FailureReason); + return; + } + + domain.DnsVerifiedAt ??= DateTimeOffset.UtcNow; + domain.VerifiedAt ??= domain.DnsVerifiedAt; + var gateway = await gatewayProvisioner.EnsureTlsAsync(domain.Host, cancellationToken); + if (!gateway.TlsReady) + { + Fail(domain, gateway.Configured, gateway.FailureReason); + return; + } + + domain.TlsReadyAt ??= DateTimeOffset.UtcNow; + domain.Status = TenantDomainStatus.Active; + domain.LastFailureReason = null; + cacheInvalidator.Invalidate(domain.TenantId); + } + + private static void Fail(TenantDomain domain, bool configured, string? reason) + { + domain.Status = configured ? TenantDomainStatus.Failed : TenantDomainStatus.Pending; + domain.LastFailureReason = reason; + } +} diff --git a/Tiku.Infrastructure/Tenancy/TenantExecutionScope.cs b/Tiku.Infrastructure/Tenancy/TenantExecutionScope.cs new file mode 100644 index 0000000..82a8cdc --- /dev/null +++ b/Tiku.Infrastructure/Tenancy/TenantExecutionScope.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Tiku.Application.Security; + +namespace Tiku.Infrastructure.Tenancy; + +public sealed class TenantExecutionScope( + IServiceScopeFactory scopeFactory, + ILogger logger) : ITenantExecutionScope +{ + public Task ExecuteAsync( + Guid? targetTenantId, + string reason, + Func operation, + CancellationToken cancellationToken = default) + { + return ExecuteAsync( + targetTenantId, + reason, + async (provider, token) => + { + await operation(provider, token); + return null; + }, + cancellationToken); + } + + public async Task ExecuteAsync( + Guid? targetTenantId, + string reason, + Func> operation, + CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(operation); + if (string.IsNullOrWhiteSpace(reason)) + { + throw new ArgumentException("A system scope requires an audit reason.", nameof(reason)); + } + + await using var scope = scopeFactory.CreateAsyncScope(); + var initializer = scope.ServiceProvider.GetRequiredService(); + initializer.InitializeSystem(targetTenantId, reason); + logger.LogWarning( + "Entering audited system tenant scope. TargetTenantId={TargetTenantId} Reason={Reason}", + targetTenantId, + reason); + + return await operation(scope.ServiceProvider, cancellationToken); + } +} diff --git a/Tiku.Infrastructure/Tenancy/TenantFrontendConfigService.cs b/Tiku.Infrastructure/Tenancy/TenantFrontendConfigService.cs new file mode 100644 index 0000000..4e546ff --- /dev/null +++ b/Tiku.Infrastructure/Tenancy/TenantFrontendConfigService.cs @@ -0,0 +1,207 @@ +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; +using Tiku.Application.Tenancy; +using Tiku.Domain.Common; +using Tiku.Domain.Tenancy; +using Tiku.Infrastructure.Persistence; + +namespace Tiku.Infrastructure.Tenancy; + +public sealed class TenantFrontendConfigService( + TikuDbContext dbContext, + IMemoryCache cache) : ITenantFrontendConfigService +{ + private static readonly TimeSpan RuntimeCacheDuration = TimeSpan.FromMinutes(2); + + public async Task GetAsync( + Guid tenantId, + CancellationToken cancellationToken = default) + { + var config = await dbContext.TenantFrontendConfigs.AsNoTracking() + .SingleOrDefaultAsync(item => item.TenantId == tenantId, cancellationToken); + return ToItem(config ?? CreateDefault(tenantId)); + } + + public async Task SaveDraftAsync( + Guid tenantId, + TenantFrontendConfigDraft draft, + CancellationToken cancellationToken = default) + { + Validate(draft); + var config = await dbContext.TenantFrontendConfigs + .SingleOrDefaultAsync(item => item.TenantId == tenantId, cancellationToken); + if (config is null) + { + config = CreateDefault(tenantId); + dbContext.TenantFrontendConfigs.Add(config); + } + + config.DraftBranding = draft.Branding.Clone(); + config.DraftTheme = draft.Theme.Clone(); + config.DraftFeatures = draft.Features.Clone(); + config.DraftNavigation = draft.Navigation.Clone(); + config.DraftHomeModules = draft.HomeModules.Clone(); + await dbContext.SaveChangesAsync(cancellationToken); + return ToItem(config); + } + + public async Task PublishAsync( + Guid tenantId, + int expectedVersion, + CancellationToken cancellationToken = default) + { + var config = await dbContext.TenantFrontendConfigs + .SingleOrDefaultAsync(item => item.TenantId == tenantId, cancellationToken) + ?? throw new TenantFrontendConfigException( + "frontend_config_not_found", + "Save a frontend configuration draft before publishing."); + if (config.ConfigVersion != expectedVersion) + { + throw new TenantFrontendConfigException( + "frontend_config_version_conflict", + "Frontend configuration has changed; reload it before publishing."); + } + + var draft = Draft(config); + Validate(draft); + config.PublishedBranding = config.DraftBranding.Clone(); + config.PublishedTheme = config.DraftTheme.Clone(); + config.PublishedFeatures = config.DraftFeatures.Clone(); + config.PublishedNavigation = config.DraftNavigation.Clone(); + config.PublishedHomeModules = config.DraftHomeModules.Clone(); + config.ConfigVersion++; + config.PublishedAt = DateTimeOffset.UtcNow; + await dbContext.SaveChangesAsync(cancellationToken); + cache.Remove(CacheKey(tenantId)); + return ToItem(config); + } + + public async Task GetRuntimeAsync( + Guid tenantId, + CancellationToken cancellationToken = default) + { + if (cache.TryGetValue(CacheKey(tenantId), out var cached) && cached is not null) + { + return cached; + } + + var tenant = await dbContext.Tenants.AsNoTracking().SingleOrDefaultAsync( + item => item.Id == tenantId && item.Status == TenantStatus.Active, + cancellationToken) + ?? throw new TenantFrontendConfigException("tenant_not_found", "Active tenant was not found."); + var config = await dbContext.TenantFrontendConfigs.AsNoTracking() + .SingleOrDefaultAsync(item => item.TenantId == tenantId, cancellationToken) + ?? CreateDefault(tenantId); + var result = new TenantRuntimeBootstrap( + config.SchemaVersion, + config.ConfigVersion, + tenant.Slug, + tenant.Name, + config.PublishedBranding.Clone(), + config.PublishedTheme.Clone(), + config.PublishedFeatures.Clone(), + config.PublishedNavigation.Clone(), + config.PublishedHomeModules.Clone()); + cache.Set(CacheKey(tenantId), result, RuntimeCacheDuration); + return result; + } + + private static TenantFrontendConfig CreateDefault(Guid tenantId) + { + return new TenantFrontendConfig + { + TenantId = tenantId, + SchemaVersion = 1, + ConfigVersion = 1 + }; + } + + private static TenantFrontendConfigItem ToItem(TenantFrontendConfig config) + { + return new TenantFrontendConfigItem( + config.SchemaVersion, + config.ConfigVersion, + new TenantFrontendConfigDraft( + config.PublishedBranding.Clone(), + config.PublishedTheme.Clone(), + config.PublishedFeatures.Clone(), + config.PublishedNavigation.Clone(), + config.PublishedHomeModules.Clone()), + Draft(config), + config.PublishedAt); + } + + private static TenantFrontendConfigDraft Draft(TenantFrontendConfig config) + { + return new TenantFrontendConfigDraft( + config.DraftBranding.Clone(), + config.DraftTheme.Clone(), + config.DraftFeatures.Clone(), + config.DraftNavigation.Clone(), + config.DraftHomeModules.Clone()); + } + + private static void Validate(TenantFrontendConfigDraft draft) + { + RequireKind(draft.Branding, JsonValueKind.Object, "branding"); + RequireKind(draft.Theme, JsonValueKind.Object, "theme"); + RequireKind(draft.Features, JsonValueKind.Object, "features"); + RequireKind(draft.Navigation, JsonValueKind.Array, "navigation"); + RequireKind(draft.HomeModules, JsonValueKind.Array, "homeModules"); + + foreach (var root in new[] { draft.Branding, draft.Theme, draft.Features, draft.Navigation, draft.HomeModules }) + { + RejectUnsafeContent(root); + } + } + + private static void RequireKind(JsonElement value, JsonValueKind expected, string field) + { + if (value.ValueKind != expected) + { + throw new TenantFrontendConfigException( + "frontend_config_invalid", + $"Frontend configuration field '{field}' must be a JSON {expected.ToString().ToLowerInvariant()}."); + } + } + + private static void RejectUnsafeContent(JsonElement value) + { + if (value.ValueKind == JsonValueKind.Object) + { + foreach (var property in value.EnumerateObject()) + { + if (property.Name.Contains("script", StringComparison.OrdinalIgnoreCase) || + property.Name.Contains("html", StringComparison.OrdinalIgnoreCase)) + { + throw UnsafeConfig(); + } + + RejectUnsafeContent(property.Value); + } + } + else if (value.ValueKind == JsonValueKind.Array) + { + foreach (var item in value.EnumerateArray()) + { + RejectUnsafeContent(item); + } + } + else if (value.ValueKind == JsonValueKind.String) + { + var text = value.GetString() ?? string.Empty; + if (text.Contains(" new( + "frontend_config_unsafe", + "Frontend configuration cannot contain HTML or executable scripts."); + + private static string CacheKey(Guid tenantId) => $"tenant-runtime:{tenantId:N}"; +} diff --git a/Tiku.Infrastructure/TenantAdmin/TenantAdminDirectService.cs b/Tiku.Infrastructure/TenantAdmin/TenantAdminDirectService.cs index d5a0cac..e629801 100644 --- a/Tiku.Infrastructure/TenantAdmin/TenantAdminDirectService.cs +++ b/Tiku.Infrastructure/TenantAdmin/TenantAdminDirectService.cs @@ -1933,6 +1933,10 @@ public sealed class TenantAdminDirectService(TikuDbContext dbContext) : ITenantA item.IsPrimary, item.VerificationToken, item.VerifiedAt, + item.LastCheckedAt, + item.DnsVerifiedAt, + item.TlsReadyAt, + item.LastFailureReason, item.CreatedAt, item.UpdatedAt); } diff --git a/Tiku.IntegrationTests/Api/ApiTestFactory.cs b/Tiku.IntegrationTests/Api/ApiTestFactory.cs index b50d3fc..0cf8061 100644 --- a/Tiku.IntegrationTests/Api/ApiTestFactory.cs +++ b/Tiku.IntegrationTests/Api/ApiTestFactory.cs @@ -1,13 +1,17 @@ using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Npgsql; using Tiku.Application.Commerce; using Tiku.Application.Auth; using Tiku.Application.Growth; using Tiku.Application.Storage; +using Tiku.Application.Security; +using Tiku.Application.Tenancy; using Tiku.Domain.Identity; using Tiku.Domain.QuestionBanks; +using Tiku.Domain.Content; using Tiku.Domain.Tenancy; using Tiku.IntegrationTests.Infrastructure; using Tiku.Infrastructure.Persistence; @@ -18,7 +22,9 @@ public sealed class ApiTestFactory( IWechatOAuthClient? wechatOAuthClient = null, IObjectStorageService? objectStorageService = null, IReferralQrcodeGenerator? referralQrcodeGenerator = null, - IPaymentProviderGateway? paymentProviderGateway = null) : WebApplicationFactory + IPaymentProviderGateway? paymentProviderGateway = null, + IDomainOwnershipVerifier? domainOwnershipVerifier = null, + IDomainGatewayProvisioner? domainGatewayProvisioner = null) : WebApplicationFactory { private readonly PostgresTestDatabase database = PostgresTestDatabase.Create(); @@ -29,6 +35,7 @@ public sealed class ApiTestFactory( foreach (var descriptor in services .Where(descriptor => descriptor.ServiceType == typeof(NpgsqlDataSource) || + descriptor.ServiceType == typeof(TenantIsolationSaveChangesInterceptor) || descriptor.ServiceType == typeof(DbContextOptions) || descriptor.ServiceType.FullName?.Contains(nameof(TikuDbContext), StringComparison.Ordinal) == true) .ToArray()) @@ -37,11 +44,13 @@ public sealed class ApiTestFactory( } services.AddSingleton(_ => NpgsqlDataSource.Create(database.ConnectionString)); - services.AddDbContextPool((serviceProvider, options) => + services.AddScoped(); + services.AddDbContext((serviceProvider, options) => { var dataSource = serviceProvider.GetRequiredService(); options.UseNpgsql(dataSource, npgsql => npgsql.MigrationsAssembly(typeof(TikuDbContext).Assembly.FullName)); + options.AddInterceptors(serviceProvider.GetRequiredService()); }); if (wechatOAuthClient is not null) @@ -63,17 +72,47 @@ public sealed class ApiTestFactory( { services.AddSingleton(paymentProviderGateway); } + + if (domainOwnershipVerifier is not null) + { + services.RemoveAll(); + services.AddSingleton(domainOwnershipVerifier); + } + + if (domainGatewayProvisioner is not null) + { + services.RemoveAll(); + services.AddSingleton(domainGatewayProvisioner); + } }); } public async Task SeedAsync(params object[] entities) { using var scope = Services.CreateScope(); + scope.ServiceProvider.GetRequiredService() + .InitializeSystem(null, "Integration test fixture seeding"); var dbContext = scope.ServiceProvider.GetRequiredService(); dbContext.AddRange(entities); await dbContext.SaveChangesAsync(); } + public IServiceScope CreateSystemScope(string reason = "Integration test verification") + { + var scope = Services.CreateScope(); + scope.ServiceProvider.GetRequiredService() + .InitializeSystem(null, reason); + return scope; + } + + public IServiceScope CreateTenantScope(Guid tenantId, string? tenantCode = null) + { + var scope = Services.CreateScope(); + scope.ServiceProvider.GetRequiredService() + .Initialize(tenantId, tenantCode, TenantResolutionSource.TenantCode); + return scope; + } + public async Task SeedQuestionWithVersionAsync(Question question, QuestionVersion version) { question.CurrentVersionId = null; @@ -81,6 +120,8 @@ public sealed class ApiTestFactory( await SeedAsync(version); using var scope = Services.CreateScope(); + scope.ServiceProvider.GetRequiredService() + .InitializeSystem(question.TenantId, "Integration test question version linking"); var dbContext = scope.ServiceProvider.GetRequiredService(); var persistedQuestion = await dbContext.Questions.SingleAsync(item => item.TenantId == question.TenantId && item.Id == question.Id); @@ -88,6 +129,24 @@ public sealed class ApiTestFactory( await dbContext.SaveChangesAsync(); } + public async Task SeedQuestionReferenceAsync( + Guid tenantId, + Guid questionOwnerTenantId, + Guid questionId, + QuestionSource source = QuestionSource.Tenant) + { + var reference = new TenantQuestionReference + { + Id = Guid.NewGuid(), + TenantId = tenantId, + QuestionOwnerTenantId = questionOwnerTenantId, + QuestionId = questionId, + Source = source + }; + await SeedAsync(reference); + return reference; + } + public async Task SeedActiveSessionAsync( Guid userId, Guid? tenantId = null, @@ -117,6 +176,8 @@ public sealed class ApiTestFactory( }); using var scope = Services.CreateScope(); + scope.ServiceProvider.GetRequiredService() + .InitializeSystem(resolvedTenantId, "Integration test session lookup"); var dbContext = scope.ServiceProvider.GetRequiredService(); return await dbContext.AuthSessions .Where(session => session.UserId == userId) diff --git a/Tiku.IntegrationTests/Api/AssetAccessEndpointTests.cs b/Tiku.IntegrationTests/Api/AssetAccessEndpointTests.cs index bab1b15..bcd00e5 100644 --- a/Tiku.IntegrationTests/Api/AssetAccessEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/AssetAccessEndpointTests.cs @@ -33,7 +33,7 @@ public sealed class AssetAccessEndpointTests Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("https://storage.example.test/download.pdf", body.RootElement.GetProperty("url").GetProperty("url").GetString()); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.Equal(1, dbContext.ContentAssets.Single(asset => asset.Id == assetId).DownloadCount); Assert.Contains(dbContext.ContentAssetAccessEvents, item => @@ -251,7 +251,7 @@ public sealed class AssetAccessEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/AssetManagementEndpointTests.cs b/Tiku.IntegrationTests/Api/AssetManagementEndpointTests.cs index c4bcd1f..3ca7a20 100644 --- a/Tiku.IntegrationTests/Api/AssetManagementEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/AssetManagementEndpointTests.cs @@ -66,7 +66,7 @@ public sealed class AssetManagementEndpointTests var listItem = Assert.Single(list.RootElement.GetProperty("items").EnumerateArray()); Assert.Equal(assetId, listItem.GetProperty("id").GetGuid()); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); var asset = dbContext.ContentAssets.Single(asset => asset.Id == assetId); Assert.Equal(seed.TenantId, asset.TenantId); @@ -118,7 +118,7 @@ public sealed class AssetManagementEndpointTests Assert.Equal("Verified", body.RootElement.GetProperty("item").GetProperty("uploadStatus").GetString()); Assert.Equal(2048, body.RootElement.GetProperty("metadata").GetProperty("sizeBytes").GetInt64()); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); var asset = dbContext.ContentAssets.Single(asset => asset.Id == assetId); Assert.Equal(AssetUploadStatus.Verified, asset.UploadStatus); @@ -299,7 +299,7 @@ public sealed class AssetManagementEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/AuthEndpointTests.cs b/Tiku.IntegrationTests/Api/AuthEndpointTests.cs index de5620f..d10cd5e 100644 --- a/Tiku.IntegrationTests/Api/AuthEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/AuthEndpointTests.cs @@ -13,6 +13,60 @@ namespace Tiku.IntegrationTests.Api; public sealed class AuthEndpointTests { + [Fact] + public async Task Custom_host_rejects_jwt_from_another_tenant_and_ignores_spoofed_tenant_header() + { + await using var factory = new ApiTestFactory(); + var tenantB = await SeedLoginUserAsync(factory); + var tenantAId = Guid.NewGuid(); + await factory.SeedAsync( + new Tenant + { + Id = tenantAId, + Slug = "tenant-a", + Name = "Tenant A", + Status = TenantStatus.Active, + Mode = TenantMode.Saas + }, + new TenantDomain + { + TenantId = tenantAId, + Host = "a.example.test", + DomainType = TenantDomainType.Custom, + Status = TenantDomainStatus.Active, + IsPrimary = true + }); + using var client = factory.CreateClient(); + var loginResponse = await client.PostAsJsonAsync( + "/api/auth/login/password", + new PasswordLoginDto + { + TenantCode = tenantB.TenantId.ToString("N"), + Phone = tenantB.Phone, + Password = "passw0rd!" + }); + var loginJson = await ReadJsonAsync(loginResponse); + var accessToken = loginJson.RootElement.GetProperty("tokens").GetProperty("accessToken").GetString(); + + using var jwtRequest = new HttpRequestMessage(HttpMethod.Get, "/api/me"); + jwtRequest.Headers.Host = "a.example.test"; + jwtRequest.Headers.Authorization = new("Bearer", accessToken); + var jwtResponse = await client.SendAsync(jwtRequest); + + using var spoofRequest = new HttpRequestMessage(HttpMethod.Post, "/api/auth/login/password"); + spoofRequest.Headers.Host = "a.example.test"; + spoofRequest.Headers.Add("x-tenant-code", tenantB.TenantId.ToString("N")); + spoofRequest.Content = JsonContent.Create(new PasswordLoginDto + { + Phone = tenantB.Phone, + Password = "passw0rd!" + }); + var spoofResponse = await client.SendAsync(spoofRequest); + + Assert.Equal(HttpStatusCode.Forbidden, jwtResponse.StatusCode); + Assert.NotEqual(HttpStatusCode.OK, spoofResponse.StatusCode); + } + [Fact] public async Task Password_login_can_access_current_user_and_tenant() { @@ -24,7 +78,7 @@ public sealed class AuthEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); @@ -57,7 +111,7 @@ public sealed class AuthEndpointTests "/api/auth/login/sms", new SmsLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Code = "123456" }); @@ -84,7 +138,7 @@ public sealed class AuthEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); @@ -136,7 +190,7 @@ public sealed class AuthEndpointTests "/api/auth/oauth/wechat-miniapp", new OAuthCodeDto { - TenantId = tenantId, + TenantCode = tenantId.ToString("N"), Code = "wx-code" }); var loginJson = await ReadJsonAsync(loginResponse); @@ -147,7 +201,7 @@ public sealed class AuthEndpointTests client.DefaultRequestHeaders.Authorization = new("Bearer", accessToken); var meResponse = await client.GetAsync("/api/me"); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.Equal(HttpStatusCode.OK, loginResponse.StatusCode); @@ -204,7 +258,7 @@ public sealed class AuthEndpointTests string phone, string code) { - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); dbContext.SmsVerificationCodes.Add(new SmsVerificationCode { diff --git a/Tiku.IntegrationTests/Api/CommerceEndpointTests.cs b/Tiku.IntegrationTests/Api/CommerceEndpointTests.cs index a737073..4623c10 100644 --- a/Tiku.IntegrationTests/Api/CommerceEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/CommerceEndpointTests.cs @@ -272,16 +272,16 @@ public sealed class CommerceEndpointTests signatureValid = true }; var firstNotify = await client.PostAsJsonAsync( - $"/api/commerce/payments/notify/wechat-pay?tenantId={seed.TenantId}", + $"/api/commerce/payments/notify/wechat-pay?tenantCode={seed.TenantId:N}", payload); var secondNotify = await client.PostAsJsonAsync( - $"/api/commerce/payments/notify/wechat-pay?tenantId={seed.TenantId}", + $"/api/commerce/payments/notify/wechat-pay?tenantCode={seed.TenantId:N}", payload); await LoginAsync(client, seed); var entitlement = await (await client.GetAsync("/api/commerce/entitlements/current")) .Content .ReadFromJsonAsync(); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); var events = dbContext.PaymentEvents.Count(item => item.EventId == "notify-001"); @@ -302,7 +302,7 @@ public sealed class CommerceEndpointTests client.DefaultRequestHeaders.Authorization = null; var notifyResponse = await client.PostAsJsonAsync( - $"/api/commerce/payments/notify/wechat-pay?tenantId={seed.TenantId}", + $"/api/commerce/payments/notify/wechat-pay?tenantCode={seed.TenantId:N}", new { eventId = "notify-invalid", @@ -403,7 +403,7 @@ public sealed class CommerceEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/CommissionEndpointTests.cs b/Tiku.IntegrationTests/Api/CommissionEndpointTests.cs index ff1d09a..a2a0af8 100644 --- a/Tiku.IntegrationTests/Api/CommissionEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/CommissionEndpointTests.cs @@ -162,7 +162,7 @@ public sealed class CommissionEndpointTests private static async Task LoginAsync(HttpClient client, LoginSeed seed) { - var loginResponse = await client.PostAsJsonAsync("/api/auth/login/password", new PasswordLoginDto { TenantId = seed.TenantId, Phone = seed.Phone, Password = "passw0rd!" }); + var loginResponse = await client.PostAsJsonAsync("/api/auth/login/password", new PasswordLoginDto { TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); loginResponse.EnsureSuccessStatusCode(); using var loginJson = await JsonDocument.ParseAsync(await loginResponse.Content.ReadAsStreamAsync()); client.DefaultRequestHeaders.Authorization = new("Bearer", loginJson.RootElement.GetProperty("tokens").GetProperty("accessToken").GetString()); diff --git a/Tiku.IntegrationTests/Api/ContentManagementEndpointTests.cs b/Tiku.IntegrationTests/Api/ContentManagementEndpointTests.cs index 620d485..88d615c 100644 --- a/Tiku.IntegrationTests/Api/ContentManagementEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/ContentManagementEndpointTests.cs @@ -141,7 +141,7 @@ public sealed class ContentManagementEndpointTests Assert.Equal(HttpStatusCode.OK, blueprintResponse.StatusCode); Assert.Equal(collectionId, blueprintJson.RootElement.GetProperty("item").GetProperty("collectionId").GetGuid()); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.Equal(1, dbContext.QuestionCollections.Single(item => item.Id == collectionId).QuestionCount); Assert.Contains(dbContext.QuestionCollectionItems, item => item.QuestionId == questionId); @@ -231,7 +231,7 @@ public sealed class ContentManagementEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/ContentNavigationEndpointTests.cs b/Tiku.IntegrationTests/Api/ContentNavigationEndpointTests.cs index 46c096e..6ff02aa 100644 --- a/Tiku.IntegrationTests/Api/ContentNavigationEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/ContentNavigationEndpointTests.cs @@ -201,15 +201,21 @@ public sealed class ContentNavigationEndpointTests PrimaryCollectionId = collectionId, Type = "choice", Status = QuestionStatus.Archived - }, - new QuestionCollectionItem - { - Id = Guid.NewGuid(), - TenantId = tenantId, - CollectionId = collectionId, - QuestionId = archivedQuestionId, - SortOrder = 2 }); + var archivedReference = await factory.SeedQuestionReferenceAsync( + tenantId, + tenantId, + archivedQuestionId); + await factory.SeedAsync(new QuestionCollectionItem + { + Id = Guid.NewGuid(), + TenantId = tenantId, + CollectionId = collectionId, + QuestionReferenceId = archivedReference.Id, + QuestionOwnerTenantId = tenantId, + QuestionId = archivedQuestionId, + SortOrder = 2 + }); await factory.SeedQuestionWithVersionAsync( new Question { @@ -227,11 +233,17 @@ public sealed class ContentNavigationEndpointTests QuestionId = publishedQuestionId, Content = "题干" }); + var publishedReference = await factory.SeedQuestionReferenceAsync( + tenantId, + tenantId, + publishedQuestionId); await factory.SeedAsync(new QuestionCollectionItem { Id = Guid.NewGuid(), TenantId = tenantId, CollectionId = collectionId, + QuestionReferenceId = publishedReference.Id, + QuestionOwnerTenantId = tenantId, QuestionId = publishedQuestionId, SortOrder = 1, Score = 2 diff --git a/Tiku.IntegrationTests/Api/CrmEndpointTests.cs b/Tiku.IntegrationTests/Api/CrmEndpointTests.cs index a2821b0..6a84608 100644 --- a/Tiku.IntegrationTests/Api/CrmEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/CrmEndpointTests.cs @@ -55,7 +55,7 @@ public sealed class CrmEndpointTests Assert.Equal("tenant_secrets:crm:webhook:default", config!.SecretRef); Assert.Equal("RoundRobin", config.AssignmentMode); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.Contains(dbContext.TenantSecrets, item => item.TenantId == seed.TenantId && @@ -146,7 +146,7 @@ public sealed class CrmEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/DirectContentEndpointTests.cs b/Tiku.IntegrationTests/Api/DirectContentEndpointTests.cs index 81416db..82f44ea 100644 --- a/Tiku.IntegrationTests/Api/DirectContentEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/DirectContentEndpointTests.cs @@ -50,7 +50,7 @@ public sealed class DirectContentEndpointTests Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(collectionId, json.RootElement.GetProperty("item").GetProperty("primaryCollectionId").GetGuid()); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.Contains(dbContext.QuestionCollectionItems, item => item.CollectionId == collectionId && item.QuestionId == questionId); Assert.Equal(1, dbContext.QuestionCollections.Single(item => item.Id == collectionId).QuestionCount); @@ -168,7 +168,7 @@ public sealed class DirectContentEndpointTests Assert.Equal(HttpStatusCode.OK, postCheckResponse.StatusCode); Assert.NotEqual(Guid.Empty, previewJobId); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.True(dbContext.ContentImportJobs.Any(item => item.Id == executeJobId && item.InsertedCount == 1)); Assert.True(dbContext.Questions.Count(item => item.TenantId == seed.TenantId) >= 2); @@ -274,7 +274,7 @@ public sealed class DirectContentEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/LearningEndpointTests.cs b/Tiku.IntegrationTests/Api/LearningEndpointTests.cs index e8da415..05962e9 100644 --- a/Tiku.IntegrationTests/Api/LearningEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/LearningEndpointTests.cs @@ -6,6 +6,7 @@ using Tiku.Api.Contracts; using Tiku.Domain.Common; using Tiku.Domain.Content; using Tiku.Domain.Identity; +using Tiku.Domain.Learning; using Tiku.Domain.QuestionBanks; using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Auth; @@ -32,13 +33,7 @@ public sealed class LearningEndpointTests await using var factory = new ApiTestFactory(); var seed = await SeedLearningUserAsync(factory); var questionId = Guid.NewGuid(); - await factory.SeedAsync(new Question - { - Id = questionId, - TenantId = seed.TenantId, - Type = "choice", - Status = QuestionStatus.Published - }); + var sessionQuestionId = await SeedAnswerableQuestionAsync(factory, seed, questionId); using var client = factory.CreateClient(); await LoginAsync(client, seed); @@ -46,7 +41,7 @@ public sealed class LearningEndpointTests "/api/learning/answers", new SubmitAnswerDto { - QuestionId = questionId, + SessionQuestionId = sessionQuestionId, SelectedOptions = ["A"], SelfJudgedCorrect = false }); @@ -55,10 +50,10 @@ public sealed class LearningEndpointTests var wrongItems = await ReadItemsAsync(wrongResponse); Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(questionId, answer.RootElement.GetProperty("questionId").GetGuid()); + Assert.Equal(sessionQuestionId, answer.RootElement.GetProperty("sessionQuestionId").GetGuid()); Assert.False(answer.RootElement.GetProperty("isCorrect").GetBoolean()); var wrong = Assert.Single(wrongItems); - Assert.Equal(questionId, wrong.GetProperty("questionId").GetGuid()); + Assert.Equal(questionId, wrong.GetProperty("locator").GetProperty("questionId").GetGuid()); Assert.Equal(1, wrong.GetProperty("wrongCount").GetInt32()); } @@ -70,14 +65,9 @@ public sealed class LearningEndpointTests var collectionId = Guid.NewGuid(); var firstQuestionId = Guid.NewGuid(); var secondQuestionId = Guid.NewGuid(); - await factory.SeedAsync( - new QuestionCollection - { - Id = collectionId, - TenantId = seed.TenantId, - Name = "基础练习", - Status = ContentStatus.Active - }, + var firstVersionId = Guid.NewGuid(); + var secondVersionId = Guid.NewGuid(); + await factory.SeedQuestionWithVersionAsync( new Question { Id = firstQuestionId, @@ -85,6 +75,15 @@ public sealed class LearningEndpointTests Type = "choice", Status = QuestionStatus.Published }, + new QuestionVersion + { + Id = firstVersionId, + TenantId = seed.TenantId, + QuestionId = firstQuestionId, + VersionNo = 1, + Content = "first" + }); + await factory.SeedQuestionWithVersionAsync( new Question { Id = secondQuestionId, @@ -92,11 +91,47 @@ public sealed class LearningEndpointTests Type = "choice", Status = QuestionStatus.Published }, + new QuestionVersion + { + Id = secondVersionId, + TenantId = seed.TenantId, + QuestionId = secondQuestionId, + VersionNo = 1, + Content = "second" + }); + var firstReferenceId = Guid.NewGuid(); + var secondReferenceId = Guid.NewGuid(); + await factory.SeedAsync( + new TenantQuestionReference + { + Id = firstReferenceId, + TenantId = seed.TenantId, + QuestionOwnerTenantId = seed.TenantId, + QuestionId = firstQuestionId, + Source = QuestionSource.Tenant + }, + new TenantQuestionReference + { + Id = secondReferenceId, + TenantId = seed.TenantId, + QuestionOwnerTenantId = seed.TenantId, + QuestionId = secondQuestionId, + Source = QuestionSource.Tenant + }, + new QuestionCollection + { + Id = collectionId, + TenantId = seed.TenantId, + Name = "基础练习", + Status = ContentStatus.Active + }, new QuestionCollectionItem { Id = Guid.NewGuid(), TenantId = seed.TenantId, CollectionId = collectionId, + QuestionReferenceId = firstReferenceId, + QuestionOwnerTenantId = seed.TenantId, QuestionId = firstQuestionId, SortOrder = 1 }, @@ -105,6 +140,8 @@ public sealed class LearningEndpointTests Id = Guid.NewGuid(), TenantId = seed.TenantId, CollectionId = collectionId, + QuestionReferenceId = secondReferenceId, + QuestionOwnerTenantId = seed.TenantId, QuestionId = secondQuestionId, SortOrder = 2 }); @@ -124,13 +161,17 @@ public sealed class LearningEndpointTests var practiceSessionId = created.RootElement.GetProperty("id").GetGuid(); var detailResponse = await client.GetAsync($"/api/learning/practice-sessions/detail?practiceSessionId={practiceSessionId}"); var detail = await ReadJsonAsync(detailResponse); + var detailQuestions = detail.RootElement.GetProperty("questions").EnumerateArray().ToArray(); + var firstSessionQuestionId = detailQuestions.Single(item => + item.GetProperty("questionId").GetGuid() == firstQuestionId).GetProperty("sessionQuestionId").GetGuid(); + var secondSessionQuestionId = detailQuestions.Single(item => + item.GetProperty("questionId").GetGuid() == secondQuestionId).GetProperty("sessionQuestionId").GetGuid(); await client.PostAsJsonAsync( "/api/learning/answers", new SubmitAnswerDto { - QuestionId = firstQuestionId, - PracticeSessionId = practiceSessionId, + SessionQuestionId = firstSessionQuestionId, SelectedOptions = ["A"], SelfJudgedCorrect = true }); @@ -138,8 +179,7 @@ public sealed class LearningEndpointTests "/api/learning/answers", new SubmitAnswerDto { - QuestionId = secondQuestionId, - PracticeSessionId = practiceSessionId, + SessionQuestionId = secondSessionQuestionId, SelectedOptions = ["B"], SelfJudgedCorrect = false }); @@ -199,7 +239,9 @@ public sealed class LearningEndpointTests Assert.Equal(HttpStatusCode.OK, addResponse.StatusCode); Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode); - Assert.Equal(questionId, Assert.Single(itemsAfterAdd).GetProperty("questionId").GetGuid()); + Assert.Equal( + questionId, + Assert.Single(itemsAfterAdd).GetProperty("locator").GetProperty("questionId").GetGuid()); Assert.Equal(HttpStatusCode.OK, removeResponse.StatusCode); Assert.Empty(itemsAfterRemove); } @@ -304,14 +346,8 @@ public sealed class LearningEndpointTests var seed = await SeedLearningUserAsync(factory); var questionId = Guid.NewGuid(); var wordId = Guid.NewGuid(); + var sessionQuestionId = await SeedAnswerableQuestionAsync(factory, seed, questionId); await factory.SeedAsync( - new Question - { - Id = questionId, - TenantId = seed.TenantId, - Type = "choice", - Status = QuestionStatus.Published - }, new VocabularyWord { Id = wordId, @@ -326,7 +362,7 @@ public sealed class LearningEndpointTests "/api/learning/answers", new SubmitAnswerDto { - QuestionId = questionId, + SessionQuestionId = sessionQuestionId, SelectedOptions = ["A"], SelfJudgedCorrect = false }); @@ -409,6 +445,64 @@ public sealed class LearningEndpointTests return (tenantId, userId, phone); } + private static async Task SeedAnswerableQuestionAsync( + ApiTestFactory factory, + (Guid TenantId, Guid UserId, string Phone) seed, + Guid questionId) + { + var versionId = Guid.NewGuid(); + await factory.SeedQuestionWithVersionAsync( + new Question + { + Id = questionId, + TenantId = seed.TenantId, + Type = "choice", + Status = QuestionStatus.Published + }, + new QuestionVersion + { + Id = versionId, + TenantId = seed.TenantId, + QuestionId = questionId, + VersionNo = 1, + Content = "answerable question" + }); + + var referenceId = Guid.NewGuid(); + var sessionId = Guid.NewGuid(); + var sessionQuestionId = Guid.NewGuid(); + await factory.SeedAsync( + new TenantQuestionReference + { + Id = referenceId, + TenantId = seed.TenantId, + QuestionOwnerTenantId = seed.TenantId, + QuestionId = questionId, + Source = QuestionSource.Tenant + }, + new PracticeSession + { + Id = sessionId, + TenantId = seed.TenantId, + UserId = seed.UserId, + Mode = "single", + QuestionCount = 1, + ExpiresAt = DateTimeOffset.UtcNow.AddMinutes(30) + }, + new PracticeSessionQuestion + { + Id = sessionQuestionId, + TenantId = seed.TenantId, + PracticeSessionId = sessionId, + QuestionReferenceId = referenceId, + QuestionOwnerTenantId = seed.TenantId, + QuestionId = questionId, + QuestionVersionId = versionId, + Position = 0 + }); + return sessionQuestionId; + } + private static async Task LoginAsync( HttpClient client, (Guid TenantId, Guid UserId, string Phone) seed) @@ -417,7 +511,7 @@ public sealed class LearningEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/PointsEndpointTests.cs b/Tiku.IntegrationTests/Api/PointsEndpointTests.cs index 3422f99..e238f35 100644 --- a/Tiku.IntegrationTests/Api/PointsEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/PointsEndpointTests.cs @@ -159,7 +159,7 @@ public sealed class PointsEndpointTests Assert.Equal(30, summary.SpentPoints); Assert.Equal(70, summary.BalancePoints); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.Contains(dbContext.Entitlements, item => item.TenantId == seed.TenantId && @@ -252,7 +252,7 @@ public sealed class PointsEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/ProfileEndpointTests.cs b/Tiku.IntegrationTests/Api/ProfileEndpointTests.cs index 2e48c69..08ceeef 100644 --- a/Tiku.IntegrationTests/Api/ProfileEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/ProfileEndpointTests.cs @@ -183,7 +183,7 @@ public sealed class ProfileEndpointTests Assert.Equal(HttpStatusCode.OK, feedbacksResponse.StatusCode); Assert.Single(feedbacksJson.RootElement.EnumerateArray()); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.Equal(NotificationStatus.Read, dbContext.UserNotifications.Single(item => item.Id == notificationId).Status); Assert.Single(dbContext.ReportStatusEvents); @@ -237,7 +237,7 @@ public sealed class ProfileEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/QuestionBankEndpointTests.cs b/Tiku.IntegrationTests/Api/QuestionBankEndpointTests.cs index 40144ff..25859d1 100644 --- a/Tiku.IntegrationTests/Api/QuestionBankEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/QuestionBankEndpointTests.cs @@ -97,11 +97,17 @@ public sealed class QuestionBankEndpointTests VersionNo = 2, Content = "题干关键词" }); + var questionReference = await factory.SeedQuestionReferenceAsync( + tenantId, + tenantId, + includedQuestionId); await factory.SeedAsync(new QuestionCollectionItem { Id = Guid.NewGuid(), TenantId = tenantId, CollectionId = collectionId, + QuestionReferenceId = questionReference.Id, + QuestionOwnerTenantId = tenantId, QuestionId = includedQuestionId }); using var client = factory.CreateClient(); diff --git a/Tiku.IntegrationTests/Api/ReferralEndpointTests.cs b/Tiku.IntegrationTests/Api/ReferralEndpointTests.cs index 3684875..5789c64 100644 --- a/Tiku.IntegrationTests/Api/ReferralEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/ReferralEndpointTests.cs @@ -114,7 +114,7 @@ public sealed class ReferralEndpointTests Assert.Equal($"ref={qrcodeItem.RefCode}", qrcodeGenerator.LastRequest.Scene); Assert.Equal("fake-referral-qrcode-generator", qrcodeItem.Metadata.GetProperty("generatedBy").GetString()); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.Contains(dbContext.ReferralLeads, item => item.TenantId == seed.TenantId && @@ -294,7 +294,7 @@ public sealed class ReferralEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/SecurityFoundationTests.cs b/Tiku.IntegrationTests/Api/SecurityFoundationTests.cs index f94342a..1a91ad5 100644 --- a/Tiku.IntegrationTests/Api/SecurityFoundationTests.cs +++ b/Tiku.IntegrationTests/Api/SecurityFoundationTests.cs @@ -35,14 +35,15 @@ public sealed class SecurityFoundationTests { await using var factory = CreateFactory(); var userId = Guid.NewGuid(); - var sessionId = await factory.SeedActiveSessionAsync(userId); + var tenantId = Guid.NewGuid(); + var sessionId = await factory.SeedActiveSessionAsync(userId, tenantId); using var client = factory.CreateClient(); client.DefaultRequestHeaders.Authorization = new( "Bearer", CreateToken([ new Claim(TikuClaimTypes.UserId, userId.ToString()), new Claim(TikuClaimTypes.SessionId, sessionId.ToString()), - new Claim(TikuClaimTypes.TenantId, Guid.NewGuid().ToString()), + new Claim(TikuClaimTypes.TenantId, tenantId.ToString()), new Claim(TikuClaimTypes.TenantRole, TenantRole.Student.ToString()) ])); @@ -56,13 +57,15 @@ public sealed class SecurityFoundationTests { var userId = Guid.NewGuid(); await using var factory = CreateFactory(); - var sessionId = await factory.SeedActiveSessionAsync(userId); + var tenantId = Guid.NewGuid(); + var sessionId = await factory.SeedActiveSessionAsync(userId, tenantId); using var client = factory.CreateClient(); client.DefaultRequestHeaders.Authorization = new( "Bearer", CreateToken([ new Claim(TikuClaimTypes.UserId, userId.ToString()), - new Claim(TikuClaimTypes.SessionId, sessionId.ToString()) + new Claim(TikuClaimTypes.SessionId, sessionId.ToString()), + new Claim(TikuClaimTypes.TenantId, tenantId.ToString()) ])); var response = await client.GetAsync("/api/_security/authenticated"); diff --git a/Tiku.IntegrationTests/Api/TenantAdminDirectEndpointTests.cs b/Tiku.IntegrationTests/Api/TenantAdminDirectEndpointTests.cs index d99f5b0..64a808c 100644 --- a/Tiku.IntegrationTests/Api/TenantAdminDirectEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/TenantAdminDirectEndpointTests.cs @@ -66,7 +66,7 @@ public sealed class TenantAdminDirectEndpointTests Assert.Equal(studentUserId, listJson.RootElement.GetProperty("items")[0].GetProperty("userId").GetGuid()); Assert.Equal(1, classesJson.RootElement.GetProperty("items")[0].GetProperty("studentCount").GetInt32()); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.True(await dbContext.StudentProfiles.AnyAsync(profile => profile.TenantId == seed.TenantId && profile.UserId == studentUserId)); Assert.True(await dbContext.TenantMemberships.AnyAsync(member => member.TenantId == seed.TenantId && member.UserId == studentUserId && member.Role == TenantRole.Student)); @@ -148,7 +148,7 @@ public sealed class TenantAdminDirectEndpointTests }); Assert.Equal(HttpStatusCode.OK, statusResponse.StatusCode); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.True(await dbContext.AuthSessions.AnyAsync(session => session.UserId == studentId && session.RevokedAt != null)); } @@ -239,7 +239,7 @@ public sealed class TenantAdminDirectEndpointTests Assert.Equal(HttpStatusCode.OK, authProviderResponse.StatusCode); Assert.Equal(HttpStatusCode.BadRequest, secretSettingsResponse.StatusCode); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.True(await dbContext.TenantBrandings.AnyAsync(item => item.TenantId == seed.TenantId && item.BrandName == "机构题库")); Assert.True(await dbContext.TenantThemeConfigs.AnyAsync(item => item.TenantId == seed.TenantId && item.ActiveTemplateCode == "classic")); @@ -377,7 +377,7 @@ public sealed class TenantAdminDirectEndpointTests Assert.True(notificationsJson.RootElement.GetProperty("items").GetArrayLength() >= 2); Assert.Single(feedbackJson.RootElement.GetProperty("items").EnumerateArray()); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.True(await dbContext.UserBadges.AnyAsync(item => item.TenantId == seed.TenantId && item.UserId == studentId && item.BadgeId == badgeId)); Assert.True(await dbContext.UserNotifications.AnyAsync(item => item.TenantId == seed.TenantId && item.UserId == studentId && item.NotificationType == "badge_granted")); @@ -435,7 +435,7 @@ public sealed class TenantAdminDirectEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/TenantCommerceEndpointTests.cs b/Tiku.IntegrationTests/Api/TenantCommerceEndpointTests.cs index fd8ff3e..1afc4e6 100644 --- a/Tiku.IntegrationTests/Api/TenantCommerceEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/TenantCommerceEndpointTests.cs @@ -95,7 +95,7 @@ public sealed class TenantCommerceEndpointTests Assert.Equal(HttpStatusCode.OK, accountsResponse.StatusCode); Assert.Contains("wechat_pay", await accountsResponse.Content.ReadAsStringAsync(), StringComparison.OrdinalIgnoreCase); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); var storedSecret = await dbContext.TenantSecrets .AsNoTracking() @@ -143,7 +143,7 @@ public sealed class TenantCommerceEndpointTests Assert.Equal(2, codes.Items.Count); Assert.Equal(HttpStatusCode.OK, redeemResponse.StatusCode); - using var scope = factory.Services.CreateScope(); + using var scope = factory.CreateSystemScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); Assert.Contains(dbContext.Entitlements, item => item.TenantId == seed.TenantId && @@ -247,7 +247,7 @@ public sealed class TenantCommerceEndpointTests "/api/auth/login/password", new PasswordLoginDto { - TenantId = seed.TenantId, + TenantCode = seed.TenantId.ToString("N"), Phone = seed.Phone, Password = "passw0rd!" }); diff --git a/Tiku.IntegrationTests/Api/TenantPublicEndpointTests.cs b/Tiku.IntegrationTests/Api/TenantPublicEndpointTests.cs index 7b0101b..eb37baa 100644 --- a/Tiku.IntegrationTests/Api/TenantPublicEndpointTests.cs +++ b/Tiku.IntegrationTests/Api/TenantPublicEndpointTests.cs @@ -1,6 +1,8 @@ using System.Net; using System.Net.Http.Json; using System.Text.Json; +using Microsoft.Extensions.DependencyInjection; +using Tiku.Application.Tenancy; using Tiku.Domain.Common; using Tiku.Domain.Operations; using Tiku.Domain.Tenancy; @@ -9,6 +11,59 @@ namespace Tiku.IntegrationTests.Api; public sealed class TenantPublicEndpointTests { + [Fact] + public async Task Runtime_bootstrap_uses_published_config_etag_and_invalidates_after_publish() + { + await using var factory = new ApiTestFactory(); + var tenantId = Guid.NewGuid(); + await SeedTenantAsync(factory, tenantId); + await factory.SeedAsync(new TenantFrontendConfig + { + TenantId = tenantId, + ConfigVersion = 1, + PublishedBranding = JsonSerializer.SerializeToElement(new { name = "published" }), + DraftBranding = JsonSerializer.SerializeToElement(new { name = "published" }) + }); + using var client = factory.CreateClient(); + + using var firstRequest = new HttpRequestMessage(HttpMethod.Get, "/api/runtime/bootstrap"); + firstRequest.Headers.Host = "student.example.test"; + var first = await client.SendAsync(firstRequest); + var firstBody = await first.Content.ReadFromJsonAsync(); + var firstEtag = first.Headers.ETag?.Tag; + + using var notModifiedRequest = new HttpRequestMessage(HttpMethod.Get, "/api/runtime/bootstrap"); + notModifiedRequest.Headers.Host = "student.example.test"; + notModifiedRequest.Headers.TryAddWithoutValidation("If-None-Match", firstEtag); + var notModified = await client.SendAsync(notModifiedRequest); + + using (var scope = factory.CreateTenantScope(tenantId, "master")) + { + var service = scope.ServiceProvider.GetRequiredService(); + await service.SaveDraftAsync( + tenantId, + new TenantFrontendConfigDraft( + JsonSerializer.SerializeToElement(new { name = "draft" }), + JsonDefaults.Object(), + JsonDefaults.Object(), + JsonDefaults.Array(), + JsonDefaults.Array())); + await service.PublishAsync(tenantId, 1); + } + + using var publishedRequest = new HttpRequestMessage(HttpMethod.Get, "/api/runtime/bootstrap"); + publishedRequest.Headers.Host = "student.example.test"; + var published = await client.SendAsync(publishedRequest); + var publishedBody = await published.Content.ReadFromJsonAsync(); + + Assert.Equal(HttpStatusCode.OK, first.StatusCode); + Assert.Equal("published", firstBody.GetProperty("branding").GetProperty("name").GetString()); + Assert.Equal(HttpStatusCode.NotModified, notModified.StatusCode); + Assert.Equal(HttpStatusCode.OK, published.StatusCode); + Assert.NotEqual(firstEtag, published.Headers.ETag?.Tag); + Assert.Equal("draft", publishedBody.GetProperty("branding").GetProperty("name").GetString()); + } + [Fact] public async Task Resolve_returns_public_tenant_configuration_by_tenant_code() { diff --git a/Tiku.IntegrationTests/ArchitectureBoundaryTests.cs b/Tiku.IntegrationTests/ArchitectureBoundaryTests.cs new file mode 100644 index 0000000..b1a2347 --- /dev/null +++ b/Tiku.IntegrationTests/ArchitectureBoundaryTests.cs @@ -0,0 +1,52 @@ +namespace Tiku.IntegrationTests; + +public sealed class ArchitectureBoundaryTests +{ + [Fact] + public void Business_code_does_not_bypass_tenant_query_boundaries() + { + var root = FindRepositoryRoot(); + var sourceRoots = new[] { "Tiku.Api", "Tiku.Application", "Tiku.Infrastructure" }; + var forbidden = new[] + { + "IgnoreQueryFilters(", + "FromSql(", + "FromSqlRaw(", + "FromSqlInterpolated(", + "ExecuteSql(", + "ExecuteSqlRaw(", + "ExecuteSqlInterpolated(", + "new NpgsqlCommand(" + }; + + var violations = sourceRoots + .SelectMany(directory => Directory.EnumerateFiles( + Path.Combine(root, directory), + "*.cs", + SearchOption.AllDirectories)) + .Where(path => !path.Contains( + $"{Path.DirectorySeparatorChar}Persistence{Path.DirectorySeparatorChar}Migrations{Path.DirectorySeparatorChar}", + StringComparison.Ordinal)) + .SelectMany(path => File.ReadLines(path) + .Select((line, index) => new { path, line, lineNumber = index + 1 })) + .Where(candidate => forbidden.Any(symbol => + candidate.line.Contains(symbol, StringComparison.Ordinal))) + .Select(candidate => $"{Path.GetRelativePath(root, candidate.path)}:{candidate.lineNumber}") + .ToArray(); + + Assert.True( + violations.Length == 0, + $"Forbidden tenant-boundary bypasses were found:{Environment.NewLine}{string.Join(Environment.NewLine, violations)}"); + } + + private static string FindRepositoryRoot() + { + var directory = new DirectoryInfo(AppContext.BaseDirectory); + while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "TIKU-BACKEND.slnx"))) + { + directory = directory.Parent; + } + + return directory?.FullName ?? throw new DirectoryNotFoundException("Repository root was not found."); + } +} diff --git a/Tiku.IntegrationTests/PersistenceModelTests.cs b/Tiku.IntegrationTests/PersistenceModelTests.cs index dfa6464..864490b 100644 --- a/Tiku.IntegrationTests/PersistenceModelTests.cs +++ b/Tiku.IntegrationTests/PersistenceModelTests.cs @@ -31,8 +31,11 @@ public sealed class PersistenceModelTests .Select(entity => entity.GetTableName()) .ToHashSet(StringComparer.Ordinal); - Assert.Equal(133, tableNames.Count); Assert.Contains("tenants", tableNames); + Assert.Contains("tenant_frontend_configs", tableNames); + Assert.Contains("tenant_question_references", tableNames); + Assert.Contains("taxonomy_nodes", tableNames); + Assert.Contains("practice_session_questions", tableNames); Assert.Contains("tenant_settings", tableNames); Assert.Contains("content_entries", tableNames); Assert.Contains("content_nodes", tableNames); @@ -102,8 +105,7 @@ public sealed class PersistenceModelTests Assert.Contains("point_exchange_orders", tableNames); Assert.Contains("content_asset_access_events", tableNames); Assert.Contains("content_asset_security_scan_events", tableNames); - Assert.Contains("question_bank_grants", tableNames); - Assert.Contains("tenant_question_bank_adoptions", tableNames); + Assert.Contains("tenant_question_bank_preferences", tableNames); Assert.Contains("ai_recommendation_reports", tableNames); Assert.Contains("referral_tracks", tableNames); Assert.Contains("referral_codes", tableNames); @@ -361,9 +363,7 @@ public sealed class PersistenceModelTests [InlineData(typeof(ContentImportIssue), nameof(ContentImportIssue.Details), "'{}'::jsonb")] [InlineData(typeof(VideoExplanation), nameof(VideoExplanation.KnowledgeTags), "'[]'::jsonb")] [InlineData(typeof(QuestionVideo), nameof(QuestionVideo.Metadata), "'{}'::jsonb")] - [InlineData(typeof(QuestionBankGrant), nameof(QuestionBankGrant.Metadata), "'{}'::jsonb")] - [InlineData(typeof(TenantQuestionBankAdoption), nameof(TenantQuestionBankAdoption.SourceSnapshot), "'{}'::jsonb")] - [InlineData(typeof(TenantQuestionBankAdoption), nameof(TenantQuestionBankAdoption.Metadata), "'{}'::jsonb")] + [InlineData(typeof(TenantQuestionBankPreference), nameof(TenantQuestionBankPreference.Metadata), "'{}'::jsonb")] [InlineData(typeof(AiRecommendationReport), nameof(AiRecommendationReport.InputPayload), "'{}'::jsonb")] [InlineData(typeof(AiRecommendationReport), nameof(AiRecommendationReport.ContextPayload), "'{}'::jsonb")] [InlineData(typeof(AiRecommendationReport), nameof(AiRecommendationReport.ResultPayload), "'{}'::jsonb")] @@ -388,6 +388,7 @@ public sealed class PersistenceModelTests using var context = new TikuDbContext(Options); AssertHasUniqueIndex( + nameof(ContentImportItem.TenantId), nameof(ContentImportItem.JobId), nameof(ContentImportItem.RowNo)); AssertHasUniqueIndex( @@ -395,17 +396,10 @@ public sealed class PersistenceModelTests nameof(QuestionVideo.QuestionId), nameof(QuestionVideo.VideoId), nameof(QuestionVideo.VideoType)); - AssertHasUniqueIndex( - nameof(TenantQuestionBankAdoption.TenantId), - nameof(TenantQuestionBankAdoption.SourceQuestionBankId)); - - var allowedPlanCodes = context.Model.FindEntityType(typeof(QuestionBankGrant))! - .FindProperty(nameof(QuestionBankGrant.AllowedPlanCodes))!; - Assert.Equal("text[]", allowedPlanCodes.GetColumnType()); - - var allowedTenantIds = context.Model.FindEntityType(typeof(QuestionBankGrant))! - .FindProperty(nameof(QuestionBankGrant.AllowedTenantIds))!; - Assert.Equal("uuid[]", allowedTenantIds.GetColumnType()); + AssertHasUniqueIndex( + nameof(TenantQuestionBankPreference.TenantId), + nameof(TenantQuestionBankPreference.QuestionBankOwnerTenantId), + nameof(TenantQuestionBankPreference.QuestionBankId)); void AssertHasUniqueIndex(params string[] propertyNames) { @@ -635,6 +629,7 @@ public sealed class PersistenceModelTests nameof(CommerceRefundRequest.TenantId), nameof(CommerceRefundRequest.RefundNo)); AssertHasUniqueIndex( + nameof(CommerceReconciliationItem.TenantId), nameof(CommerceReconciliationItem.BatchId), nameof(CommerceReconciliationItem.RowNo)); AssertHasUniqueIndex( @@ -848,7 +843,9 @@ public sealed class PersistenceModelTests using var context = new TikuDbContext(Options); AssertHasUniqueIndex(nameof(PlatformSaasPlan.Code)); - AssertHasUniqueIndex(nameof(TenantInvoice.InvoiceNo)); + AssertHasUniqueIndex( + nameof(TenantInvoice.TenantId), + nameof(TenantInvoice.InvoiceNo)); AssertHasUniqueIndex( nameof(TenantInvoice.TenantId), nameof(TenantInvoice.BillingPeriodStart), @@ -863,6 +860,7 @@ public sealed class PersistenceModelTests AssertHasUniqueIndex(nameof(PlatformAuditAlert.RuleId), nameof(PlatformAuditAlert.AuditLogId)); AssertHasUniqueIndex(nameof(PlatformDunningNotificationChannel.ChannelCode)); AssertHasUniqueIndex( + nameof(PlatformDunningNotificationEvent.TenantId), nameof(PlatformDunningNotificationEvent.ChannelId), nameof(PlatformDunningNotificationEvent.ReminderId)); diff --git a/Tiku.IntegrationTests/PhaseThreeTenantIsolationTests.cs b/Tiku.IntegrationTests/PhaseThreeTenantIsolationTests.cs new file mode 100644 index 0000000..18e7e4d --- /dev/null +++ b/Tiku.IntegrationTests/PhaseThreeTenantIsolationTests.cs @@ -0,0 +1,358 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Tiku.Application.QuestionBanks; +using Tiku.Application.Learning; +using Tiku.Domain.Common; +using Tiku.Domain.Commerce; +using Tiku.Domain.Catalog; +using Tiku.Domain.Content; +using Tiku.Domain.QuestionBanks; +using Tiku.Domain.Identity; +using Tiku.Domain.Learning; +using Tiku.Domain.Tenancy; +using Tiku.Infrastructure.Persistence; +using Tiku.IntegrationTests.Api; + +namespace Tiku.IntegrationTests; + +public sealed class PhaseThreeTenantIsolationTests +{ + [Fact] + public async Task Unresolved_context_reads_no_tenant_rows_and_rejects_writes() + { + await using var factory = new ApiTestFactory(); + var tenantId = Guid.NewGuid(); + await factory.SeedAsync(Tenant(tenantId, "tenant-a"), new TenantBranding + { + TenantId = tenantId, + BrandName = "Tenant A" + }); + + using var scope = factory.Services.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + Assert.Empty(await dbContext.TenantBrandings.ToArrayAsync()); + dbContext.TenantBrandings.Add(new TenantBranding { BrandName = "unresolved" }); + await Assert.ThrowsAsync(() => dbContext.SaveChangesAsync()); + } + + [Fact] + public async Task Tenant_a_cannot_read_or_attach_tenant_b_data() + { + await using var factory = new ApiTestFactory(); + var tenantA = Guid.NewGuid(); + var tenantB = Guid.NewGuid(); + await factory.SeedAsync( + Tenant(tenantA, "tenant-a"), + Tenant(tenantB, "tenant-b"), + new TenantBranding { TenantId = tenantA, BrandName = "A" }, + new TenantBranding { TenantId = tenantB, BrandName = "B" }); + + using var scope = factory.CreateTenantScope(tenantA, "tenant-a"); + var dbContext = scope.ServiceProvider.GetRequiredService(); + var visible = await dbContext.TenantBrandings.AsNoTracking().ToArrayAsync(); + Assert.Equal(tenantA, Assert.Single(visible).TenantId); + + var forged = new TenantBranding { TenantId = tenantB, BrandName = "forged" }; + dbContext.Attach(forged); + forged.BrandName = "modified"; + dbContext.Entry(forged).Property(item => item.BrandName).IsModified = true; + await Assert.ThrowsAsync(() => dbContext.SaveChangesAsync()); + } + + [Fact] + public async Task PostgreSql_rejects_reference_to_another_tenant_private_question() + { + await using var factory = new ApiTestFactory(); + var tenantA = Guid.NewGuid(); + var tenantB = Guid.NewGuid(); + var questionId = Guid.NewGuid(); + await factory.SeedAsync( + Tenant(tenantA, "tenant-a"), + Tenant(tenantB, "tenant-b"), + new Question + { + Id = questionId, + TenantId = tenantB, + Type = "choice", + Status = QuestionStatus.Published + }); + + await Assert.ThrowsAsync(() => factory.SeedAsync(new TenantQuestionReference + { + TenantId = tenantA, + QuestionOwnerTenantId = tenantB, + QuestionId = questionId, + Source = QuestionSource.Tenant + })); + } + + [Fact] + public async Task Taxonomy_extension_accepts_platform_parent_and_rejects_other_tenant_parent() + { + await using var factory = new ApiTestFactory(); + var platformId = Guid.NewGuid(); + var tenantA = Guid.NewGuid(); + var tenantB = Guid.NewGuid(); + var platformNodeId = Guid.NewGuid(); + var tenantBNodeId = Guid.NewGuid(); + await factory.SeedAsync( + Tenant(platformId, "platform", TenantMode.PlatformOwned), + Tenant(tenantA, "tenant-a"), + Tenant(tenantB, "tenant-b"), + new TaxonomyNode + { + Id = platformNodeId, + TenantId = platformId, + Code = "platform-subject", + Name = "Platform Subject", + NodeType = TaxonomyNodeType.Subject + }, + new TaxonomyNode + { + Id = tenantBNodeId, + TenantId = tenantB, + Code = "tenant-b-subject", + Name = "Tenant B Subject", + NodeType = TaxonomyNodeType.Subject + }); + + await factory.SeedAsync(new TaxonomyNode + { + TenantId = tenantA, + ParentOwnerTenantId = platformId, + ParentId = platformNodeId, + Code = "tenant-a-platform-extension", + Name = "Valid Extension", + NodeType = TaxonomyNodeType.Chapter + }); + + await Assert.ThrowsAsync(() => factory.SeedAsync(new TaxonomyNode + { + TenantId = tenantA, + ParentOwnerTenantId = tenantB, + ParentId = tenantBNodeId, + Code = "tenant-a-forged-extension", + Name = "Invalid Extension", + NodeType = TaxonomyNodeType.Chapter + })); + } + + [Theory] + [InlineData(TenantSubscriptionStatus.Trial, true)] + [InlineData(TenantSubscriptionStatus.Active, true)] + [InlineData(TenantSubscriptionStatus.PastDue, false)] + [InlineData(TenantSubscriptionStatus.Cancelled, false)] + public async Task Public_question_reference_requires_current_subscription( + TenantSubscriptionStatus subscriptionStatus, + bool allowed) + { + await using var factory = new ApiTestFactory(); + var platformId = Guid.NewGuid(); + var tenantId = Guid.NewGuid(); + var questionId = Guid.NewGuid(); + await factory.SeedAsync( + Tenant(platformId, "platform", TenantMode.PlatformOwned), + Tenant(tenantId, "tenant-a"), + new Question + { + Id = questionId, + TenantId = platformId, + Type = "choice", + Status = QuestionStatus.Published + }, + new TenantSubscription + { + TenantId = tenantId, + PlanCode = "standard", + Status = subscriptionStatus, + StartsAt = DateTimeOffset.UtcNow.AddDays(-1), + ExpiresAt = DateTimeOffset.UtcNow.AddDays(1) + }); + + using var scope = factory.CreateTenantScope(tenantId, "tenant-a"); + var service = scope.ServiceProvider.GetRequiredService(); + var operation = async () => + { + var reference = await service.ResolveAsync( + tenantId, + null, + new QuestionLocator(QuestionSource.Platform, questionId)); + await scope.ServiceProvider.GetRequiredService().SaveChangesAsync(); + return reference; + }; + + if (allowed) + { + var reference = await operation(); + Assert.Equal(platformId, reference.QuestionOwnerTenantId); + } + else + { + await Assert.ThrowsAsync(operation); + } + } + + [Fact] + public async Task Mixed_collection_pins_versions_and_new_session_uses_new_platform_version() + { + await using var factory = new ApiTestFactory(); + var platformId = Guid.NewGuid(); + var tenantId = Guid.NewGuid(); + var userId = Guid.NewGuid(); + var platformQuestionId = Guid.NewGuid(); + var privateQuestionId = Guid.NewGuid(); + var platformV1 = Guid.NewGuid(); + var platformV2 = Guid.NewGuid(); + var privateV1 = Guid.NewGuid(); + var collectionId = Guid.NewGuid(); + var platformReferenceId = Guid.NewGuid(); + var privateReferenceId = Guid.NewGuid(); + await factory.SeedAsync( + Tenant(platformId, "platform", TenantMode.PlatformOwned), + Tenant(tenantId, "tenant-a"), + new User { Id = userId, Phone = "13800009999" }, + new TenantSubscription + { + TenantId = tenantId, + PlanCode = "standard", + Status = TenantSubscriptionStatus.Active, + ExpiresAt = DateTimeOffset.UtcNow.AddDays(30) + }); + await factory.SeedQuestionWithVersionAsync( + new Question + { + Id = platformQuestionId, + TenantId = platformId, + Type = "choice", + Status = QuestionStatus.Published + }, + new QuestionVersion + { + Id = platformV1, + TenantId = platformId, + QuestionId = platformQuestionId, + VersionNo = 1, + Content = "platform-v1" + }); + await factory.SeedQuestionWithVersionAsync( + new Question + { + Id = privateQuestionId, + TenantId = tenantId, + Type = "choice", + Status = QuestionStatus.Published + }, + new QuestionVersion + { + Id = privateV1, + TenantId = tenantId, + QuestionId = privateQuestionId, + VersionNo = 1, + Content = "private-v1" + }); + await factory.SeedAsync( + new TenantQuestionReference + { + Id = platformReferenceId, + TenantId = tenantId, + QuestionOwnerTenantId = platformId, + QuestionId = platformQuestionId, + Source = QuestionSource.Platform + }, + new TenantQuestionReference + { + Id = privateReferenceId, + TenantId = tenantId, + QuestionOwnerTenantId = tenantId, + QuestionId = privateQuestionId, + Source = QuestionSource.Tenant + }, + new QuestionCollection + { + Id = collectionId, + TenantId = tenantId, + Name = "mixed", + Status = ContentStatus.Active + }, + new QuestionCollectionItem + { + TenantId = tenantId, + CollectionId = collectionId, + QuestionReferenceId = platformReferenceId, + QuestionOwnerTenantId = platformId, + QuestionId = platformQuestionId, + SortOrder = 1 + }, + new QuestionCollectionItem + { + TenantId = tenantId, + CollectionId = collectionId, + QuestionReferenceId = privateReferenceId, + QuestionOwnerTenantId = tenantId, + QuestionId = privateQuestionId, + SortOrder = 2 + }); + + Guid firstSessionId; + using (var scope = factory.CreateTenantScope(tenantId, "tenant-a")) + { + var created = await scope.ServiceProvider.GetRequiredService() + .CreatePracticeSessionAsync( + new LearningActor(tenantId, userId), + new PracticeSessionCommand( + "collection", null, null, null, collectionId, null, null, 10, null, null, JsonDefaults.Object())); + firstSessionId = created.Id; + } + + await factory.SeedAsync(new QuestionVersion + { + Id = platformV2, + TenantId = platformId, + QuestionId = platformQuestionId, + VersionNo = 2, + Content = "platform-v2" + }); + using (var scope = factory.CreateSystemScope("Publish platform question V2")) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + var question = await dbContext.Questions.SingleAsync(item => + item.TenantId == platformId && item.Id == platformQuestionId); + question.CurrentVersionId = platformV2; + await dbContext.SaveChangesAsync(); + } + + Guid secondSessionId; + using (var scope = factory.CreateTenantScope(tenantId, "tenant-a")) + { + var created = await scope.ServiceProvider.GetRequiredService() + .CreatePracticeSessionAsync( + new LearningActor(tenantId, userId), + new PracticeSessionCommand( + "collection", null, null, null, collectionId, null, null, 10, null, null, JsonDefaults.Object())); + secondSessionId = created.Id; + } + + using var verificationScope = factory.CreateSystemScope(); + var verificationDb = verificationScope.ServiceProvider.GetRequiredService(); + var firstQuestions = await verificationDb.PracticeSessionQuestions + .Where(item => item.PracticeSessionId == firstSessionId) + .ToArrayAsync(); + var secondQuestions = await verificationDb.PracticeSessionQuestions + .Where(item => item.PracticeSessionId == secondSessionId) + .ToArrayAsync(); + Assert.Equal(2, firstQuestions.Length); + Assert.Equal(2, secondQuestions.Length); + Assert.Equal(platformV1, firstQuestions.Single(item => item.QuestionId == platformQuestionId).QuestionVersionId); + Assert.Equal(platformV2, secondQuestions.Single(item => item.QuestionId == platformQuestionId).QuestionVersionId); + Assert.Equal(privateV1, firstQuestions.Single(item => item.QuestionId == privateQuestionId).QuestionVersionId); + } + + private static Tenant Tenant(Guid id, string slug, TenantMode mode = TenantMode.Saas) => new() + { + Id = id, + Slug = slug, + Name = slug, + Status = TenantStatus.Active, + Mode = mode + }; +} diff --git a/Tiku.IntegrationTests/TenantDomainLifecycleTests.cs b/Tiku.IntegrationTests/TenantDomainLifecycleTests.cs new file mode 100644 index 0000000..66af0b2 --- /dev/null +++ b/Tiku.IntegrationTests/TenantDomainLifecycleTests.cs @@ -0,0 +1,114 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Tiku.Application.Tenancy; +using Tiku.Domain.Tenancy; +using Tiku.Infrastructure.Persistence; +using Tiku.IntegrationTests.Api; + +namespace Tiku.IntegrationTests; + +public sealed class TenantDomainLifecycleTests +{ + [Fact] + public async Task Domain_becomes_active_only_after_dns_and_tls_are_ready() + { + await using var factory = new ApiTestFactory( + domainOwnershipVerifier: new FakeOwnershipVerifier(true), + domainGatewayProvisioner: new FakeGatewayProvisioner(true)); + var tenantId = Guid.NewGuid(); + var domainId = Guid.NewGuid(); + await factory.SeedAsync( + new Tenant + { + Id = tenantId, + Slug = "tenant-a", + Name = "Tenant A", + Status = TenantStatus.Active, + Mode = TenantMode.Saas + }, + new TenantDomain + { + Id = domainId, + TenantId = tenantId, + Host = "learn.tenant-a.example", + DomainType = TenantDomainType.Custom, + Status = TenantDomainStatus.Pending, + VerificationToken = "verification-token" + }); + + using (var scope = factory.CreateSystemScope("Process domain lifecycle")) + { + var processed = await scope.ServiceProvider + .GetRequiredService() + .ProcessPendingAsync(); + Assert.Equal(1, processed); + } + + using var verificationScope = factory.CreateSystemScope(); + var domain = await verificationScope.ServiceProvider.GetRequiredService() + .TenantDomains.SingleAsync(item => item.Id == domainId); + Assert.Equal(TenantDomainStatus.Active, domain.Status); + Assert.NotNull(domain.DnsVerifiedAt); + Assert.NotNull(domain.TlsReadyAt); + Assert.Null(domain.LastFailureReason); + } + + [Fact] + public async Task Missing_gateway_configuration_keeps_domain_pending() + { + await using var factory = new ApiTestFactory( + domainOwnershipVerifier: new FakeOwnershipVerifier(true), + domainGatewayProvisioner: new FakeGatewayProvisioner(false, configured: false)); + var tenantId = Guid.NewGuid(); + var domainId = Guid.NewGuid(); + await factory.SeedAsync( + new Tenant + { + Id = tenantId, + Slug = "tenant-a", + Name = "Tenant A", + Status = TenantStatus.Active, + Mode = TenantMode.Saas + }, + new TenantDomain + { + Id = domainId, + TenantId = tenantId, + Host = "pending.tenant-a.example", + Status = TenantDomainStatus.Pending, + VerificationToken = "verification-token" + }); + + using (var scope = factory.CreateSystemScope("Process pending domain")) + { + await scope.ServiceProvider.GetRequiredService().ProcessPendingAsync(); + } + + using var verificationScope = factory.CreateSystemScope(); + var domain = await verificationScope.ServiceProvider.GetRequiredService() + .TenantDomains.SingleAsync(item => item.Id == domainId); + Assert.Equal(TenantDomainStatus.Pending, domain.Status); + Assert.NotNull(domain.DnsVerifiedAt); + Assert.Contains("not configured", domain.LastFailureReason, StringComparison.OrdinalIgnoreCase); + } + + private sealed class FakeOwnershipVerifier(bool verified) : IDomainOwnershipVerifier + { + public Task VerifyAsync( + string host, + string verificationToken, + CancellationToken cancellationToken = default) => + Task.FromResult(new DomainOwnershipResult(verified, true, verified ? null : "DNS failed")); + } + + private sealed class FakeGatewayProvisioner(bool ready, bool configured = true) : IDomainGatewayProvisioner + { + public Task EnsureTlsAsync( + string host, + CancellationToken cancellationToken = default) => + Task.FromResult(new DomainGatewayResult( + ready, + configured, + ready ? null : configured ? "TLS pending" : "Gateway is not configured")); + } +} diff --git a/Tiku.Worker/Program.cs b/Tiku.Worker/Program.cs index fb08465..6af43d2 100644 --- a/Tiku.Worker/Program.cs +++ b/Tiku.Worker/Program.cs @@ -1,6 +1,17 @@ +using Tiku.Application; +using Tiku.Application.Tenancy; +using Tiku.Infrastructure; using Tiku.Worker; var builder = Host.CreateApplicationBuilder(args); +var connectionString = builder.Configuration.GetConnectionString("Database") + ?? builder.Configuration["DATABASE_URL"] + ?? throw new InvalidOperationException( + "Database connection is required. Configure ConnectionStrings:Database or DATABASE_URL."); +builder.Services.AddApplication(); +builder.Services.AddInfrastructure(connectionString); +builder.Services.AddOptions() + .Bind(builder.Configuration.GetSection("TenantDomains")); builder.Services.AddHostedService(); var host = builder.Build(); diff --git a/Tiku.Worker/Worker.cs b/Tiku.Worker/Worker.cs index f9e9c94..68e2331 100644 --- a/Tiku.Worker/Worker.cs +++ b/Tiku.Worker/Worker.cs @@ -1,16 +1,43 @@ +using Microsoft.Extensions.Options; +using Tiku.Application.Security; +using Tiku.Application.Tenancy; + namespace Tiku.Worker; -public class Worker(ILogger logger) : BackgroundService +public class Worker( + IServiceScopeFactory scopeFactory, + IOptions options, + ILogger logger) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { - if (logger.IsEnabled(LogLevel.Information)) + try { - logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); + await using var scope = scopeFactory.CreateAsyncScope(); + scope.ServiceProvider.GetRequiredService() + .InitializeSystem(null, "Tenant domain DNS and TLS lifecycle worker"); + var processed = await scope.ServiceProvider + .GetRequiredService() + .ProcessPendingAsync(stoppingToken); + if (processed > 0) + { + logger.LogInformation("Processed {Count} pending tenant domains.", processed); + } } - await Task.Delay(1000, stoppingToken); + catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) + { + break; + } + catch (Exception exception) + { + logger.LogError(exception, "Tenant domain lifecycle processing failed."); + } + + await Task.Delay( + TimeSpan.FromSeconds(Math.Clamp(options.Value.PollSeconds, 10, 3600)), + stoppingToken); } } } diff --git a/Tiku.Worker/appsettings.json b/Tiku.Worker/appsettings.json index b2dcdb6..f6742fb 100644 --- a/Tiku.Worker/appsettings.json +++ b/Tiku.Worker/appsettings.json @@ -1,4 +1,14 @@ { + "TenantDomains": { + "Enabled": true, + "PollSeconds": 60, + "BatchSize": 50, + "DnsJsonEndpoint": "https://cloudflare-dns.com/dns-query", + "VerificationRecordPrefix": "_tiku-verification", + "AllowedCnameTargets": [], + "GatewayBaseUrl": null, + "GatewayApiKey": null + }, "Logging": { "LogLevel": { "Default": "Information", diff --git a/docs/migration-roadmap.md b/docs/migration-roadmap.md index 1f8cb76..cfef723 100644 --- a/docs/migration-roadmap.md +++ b/docs/migration-roadmap.md @@ -23,7 +23,8 @@ - ASP.NET Core Controller API。 - EF Core + Npgsql + PostgreSQL。 -- 单例 `NpgsqlDataSource` + `AddDbContextPool`。 +- 单例 `NpgsqlDataSource` + scoped `AddDbContext`。 +- `ITenantContext`、全局 Query Filter、写入拦截器和 PostgreSQL 组合约束。 - Serilog 结构化日志。 - CORS / RateLimiter / Options 校验。 - Scalar / OpenAPI 基础入口。 @@ -32,10 +33,10 @@ ### 数据库 -已完成 greenfield 初始 schema,并持续追加必要 migration。当前数据库模型已经覆盖: +已按无正式业务数据前提压缩为唯一 greenfield `InitialSchema`。当前数据库模型已经覆盖: - 租户、用户、成员、认证、Session、短信验证码。 -- 题库、题目、题目版本、内容树、题集、练习蓝图。 +- 平台公共题库、租户私有题库、受控题目引用、题目版本、分类主干、题集和练习蓝图。 - 词汇、手册、分数线动态字段与记录。 - 资源、图片、App 资源、视频解析、导入任务。 - 学习记录、答题、收藏、错题、报告、统计。 @@ -45,6 +46,7 @@ - 运营内容、通知、徽章、审计。 - 平台账单、催缴、审计告警、对账/退款相关模型。 - PocketBase 导入审计。 +- 自定义域名 DNS/TLS 生命周期和租户前端运行时配置。 ### 已迁移业务闭环 diff --git a/docs/migration/phase-3-tenant-isolation-and-shared-question-bank.md b/docs/migration/phase-3-tenant-isolation-and-shared-question-bank.md new file mode 100644 index 0000000..2c3b47f --- /dev/null +++ b/docs/migration/phase-3-tenant-isolation-and-shared-question-bank.md @@ -0,0 +1,199 @@ +# 第三阶段:强租户隔离、共享题库与租户前端运行时 + +状态:已实施并通过真实 PostgreSQL 验收(2026-07-27)。 + +本阶段按 greenfield 项目实施,不兼容现有错误模型,也不承担正式业务数据迁移。阶段目标不是简单给现有查询补 `TenantId`,而是同时建立请求、EF Core、写入和 PostgreSQL 四层租户边界,并让公共题库能够完整参与租户学生的组卷、答题、收藏和错题闭环。 + +原先预计的 3~5 个工作日只覆盖基础租户过滤。加入共享题库、版本锁定、域名入口和租户前端运行时后,完整阶段预计 10~14 个工作日。 + +## 已确认的产品规则 + +- 系统只存在一个 `TenantMode.PlatformOwned` 平台主体,平台主体拥有全部公共题库。 +- `Trial` 或 `Active` 且未过期的租户自动拥有全部公共题库访问权,不逐库授权。 +- 租户可以上传自己的私有题库,私有题只对本租户学生和管理员可见。 +- 公共题和租户私有题可以混合进入同一试卷、章节练习或题单。 +- 平台分类是公共主干,租户可以在公共节点下增加仅本租户可见的扩展节点。 +- 新练习使用题目当前已发布版本;已开始练习锁定原版本,历史答题永久按原版本回放。 +- `PastDue` 或 `Cancelled` 租户不能浏览公共题答案或创建新的公共题练习;未过期的既有会话可以完成,历史答题、收藏、错题和统计继续可见。 +- 租户自定义域名通过 CNAME 指向统一前端和网关,浏览器使用同域 `/api` 访问后端。 +- 第一版前端配置覆盖品牌、主题令牌、功能开关、导航和首页模块,不实现任意低代码页面或脚本注入。 +- 本阶段不引入 PostgreSQL RLS。隔离由请求上下文、EF Core Query Filter、写入拦截器、数据库约束和真实 PostgreSQL 测试共同保证。 + +## 3A:请求租户与持久化边界 + +### 请求上下文 + +使用只读 `ITenantContext` 替换可由业务代码调用 `Load` 的 `ICurrentTenant`。公开属性固定为: + +- `TenantId` +- `TenantCode` +- `ResolutionSource` +- `IsResolved` +- `IsSystem` + +只有 API 入口的内部初始化器可以设置请求租户。普通 Controller 和 Service 只能读取上下文,不能切换租户。 + +平台任务、Worker 和公共题库基础设施通过 `ITenantExecutionScope` 创建新的 DI scope。System scope 必须提供调用方和原因,记录结构化审计;不允许在现有请求 scope 内临时改写租户。 + +### EF Core 防护 + +- 生产和测试均从 `AddDbContextPool` 改为 scoped `AddDbContext`。 +- 所有租户实体实现统一 marker interface。 +- 全局 Query Filter 自动添加当前 `TenantId`;未解析租户时查询默认返回空集合。 +- 普通租户 Query Filter 永远不自动包含平台主体数据,公共内容只能通过专用服务访问。 +- `SaveChangesInterceptor` 对 Added 自动写入当前租户,并拒绝伪造租户、跨租户修改或删除以及修改已有记录的租户归属。 +- 未解析租户的普通写入直接失败;System scope 也不能把已有记录改归其他租户。 + +模型启动审计必须拒绝以下情况: + +- 实体存在 `TenantId` 却没有明确分类。 +- 租户范围内的唯一索引未包含 `TenantId`。 +- 租户实体之间的外键未包含组合租户键。 +- 新增租户实体却没有对应 Query Filter。 + +普通业务代码禁止调用 `IgnoreQueryFilters()`、`FromSql`、`ExecuteSql` 或直接创建 `NpgsqlCommand`。确有需要的实现只能位于集中基础设施边界,并由架构测试维护允许列表。 + +## 3B:域名入口与租户解析 + +租户解析必须在认证和任何租户数据库查询之前完成: + +1. 网关移除客户端提供的 `Forwarded`、`X-Forwarded-Host` 等头。 +2. 只接收受信代理重写的 Host 信息。 +3. `TenantResolutionMiddleware` 通过只读 `ITenantDirectory` 查询 Active 域名和 Active 租户。 +4. 未知、未验证、已禁用域名在认证前返回 404。 +5. JWT tenant claim 与 Host 解析结果不一致时返回 403。 + +自定义域名请求不得通过 `host`、`tenantCode` 查询参数或 `x-tenant-code` 覆盖租户。显式 tenant code 只允许平台控制域名上的登录引导接口使用,多来源冲突必须拒绝。 + +域名生命周期固定为: + +1. 创建 Pending 域名并生成验证令牌。 +2. 租户设置 CNAME 和 TXT 所有权记录。 +3. Worker 验证 DNS。 +4. 网关完成 TLS 配置。 +5. 域名变为 Active 后才参与请求解析。 + +`TenantDomain.Host` 全局唯一;每个租户最多一个 Active 主域名。解析结果使用短 TTL 缓存,域名或租户状态变化时主动失效。 + +## 3C:共享题库、租户私库与学习闭环 + +### 内容所有权 + +- 公共题库、题目和题目版本的 `TenantId` 为平台主体 ID。 +- 私有题库、题目和题目版本的 `TenantId` 为上传租户 ID。 +- 删除容易与真实所有权冲突的 `QuestionBank.SourceScope`。 +- 删除 `QuestionBankGrant` 和 `TenantQuestionBankAdoption` 作为访问控制主模型。 +- 新增 `TenantQuestionBankPreference`,只保存租户对公共题库的显示/隐藏、别名、排序和导航位置。 +- `IPublicQuestionAccessPolicy` 根据租户状态和订阅状态统一判断能否开始新的公共题练习。 + +数据库对平台主体增加唯一约束,保证只能存在一个 `PlatformOwned` 租户。 + +### 公共分类主干与租户扩展 + +分类节点具有明确的 `OwnerTenantId`。父节点引用保存 `ParentOwnerTenantId + ParentId`: + +- 平台节点只能组成公共主干。 +- 租户扩展节点可以挂到平台节点或本租户节点。 +- 不允许租户 A 的节点挂到租户 B 的节点。 +- 公共题只能关联平台分类。 +- 租户私题可以关联平台分类或本租户扩展分类。 + +所有分类、题库和题目关联使用包含所有者 ID 的组合外键,并通过 PostgreSQL 约束或集中触发器验证“平台或本租户”规则。 + +### 受控题目引用 + +新增 `TenantQuestionReference`: + +- `TenantId`:消费题目的租户。 +- `QuestionOwnerTenantId`:平台主体或当前租户。 +- `QuestionId`:实际题目。 + +三列建立唯一约束和组合外键。引用按需创建,不预生成“所有租户 × 全部公共题”。数据库只允许当前租户引用平台公共题或本租户私题,永远拒绝引用其他租户私题。 + +公开查询返回 `QuestionLocator { source, questionId }`,其中 `source` 只允许 `platform` 或 `tenant`。客户端不能提交任意 owner tenant ID;写操作由专用服务把 Locator 解析为受控题目引用。 + +### 组卷、练习和历史版本 + +- `QuestionCollectionItem` 改为引用 `TenantQuestionReference`,允许公共题和私题混合组卷。 +- 新增规范化 `PracticeSessionQuestion`,删除 `PracticeSession.QuestionIds` JSON。 +- SessionQuestion 保存题目顺序、分值、题目所有者、题目 ID 和创建会话时锁定的版本 ID。 +- 答题接口只接受 `sessionQuestionId`,不再接受裸 `QuestionId`;单题练习也创建轻量会话。 +- `AnswerRecord` 关联 SessionQuestion。 +- `FavoriteQuestion` 和 `WrongQuestion` 关联受控题目引用,不再假设学习租户和题目所有者相同。 +- 旧题目版本只能归档,不能物理删除。 + +## 3D:租户前端运行时配置 + +整合现有 Branding、Theme 和 PublicConfig,形成版本化 `TenantFrontendConfig`,配置范围包括: + +- 品牌名称、Logo、Favicon 和客服信息。 +- 颜色、字体、间距和圆角等主题令牌。 +- 学生端功能开关。 +- 导航菜单。 +- 首页模块及排序。 + +配置采用 Draft → Preview → Publish。发布使用乐观并发版本号,服务端在发布前验证结构;不接受任意 HTML、JavaScript、外部脚本或管理端功能开关进入公开配置。 + +新增 `GET /api/runtime/bootstrap`: + +- 只根据当前可信 Host 返回配置。 +- 返回 `schemaVersion`、`configVersion`、租户代码、品牌、主题、功能、导航和首页模块。 +- 不返回内部租户 ID、密钥或管理端配置。 +- 支持 ETag / 304。 +- 配置发布、域名停用或租户暂停时主动失效缓存。 + +现有 `tenant/resolve?host=` 不再作为自定义域名的公开切租户入口,只保留给受控平台登录引导流程。 + +## 3E:重建迁移与验收 + +项目尚无正式业务数据,因此不编写兼容迁移或双写逻辑: + +- 完成模型重构后删除现有 Migration 历史和 Snapshot。 +- 重新生成单一 Initial Schema。 +- 重建本地开发数据库。 +- 使用 `Tiku.DbMigrator` 从空 PostgreSQL 执行完整迁移。 +- API 和 Worker 继续禁止启动时自动执行 Migration。 + +数据库必须落地以下硬约束: + +- 唯一平台主体。 +- 域名全局唯一。 +- 租户唯一索引包含租户键。 +- 题目、版本和分类使用所有者组合键。 +- 题目引用只能指向平台或本租户。 +- SessionQuestion 的版本属于对应题目。 +- 学习记录与当前租户、学生和会话一致。 + +## 测试矩阵 + +真实 PostgreSQL 测试创建平台主体、租户 A、租户 B 及三套内容,至少覆盖: + +- A 默认不能读取、修改、删除或 Attach 伪造 B 的租户数据。 +- A 可以访问公共题和 A 私题,不能构造 B 私题引用。 +- 公共题和私题可以混合组卷,并完成答题、收藏、错题和统计。 +- 公共题 V1 创建会话后发布 V2:旧会话仍用 V1,新会话使用 V2,历史答案可回放。 +- Trial/Active 可以创建公共题练习;PastDue/Cancelled 只能完成未过期会话并查看历史。 +- 域名 A 携带租户 B JWT 时被拒绝;伪造 Host、tenantCode 和转发头不能切租户。 +- Pending 域名、未知域名和 Suspended 租户无法获取运行时配置。 +- 配置草稿不影响线上;发布后 ETag 和缓存版本改变。 +- 新增租户实体漏写 Filter、组合外键或租户索引时模型测试失败。 +- 架构测试发现未经允许的 Query Filter 绕过或原生 SQL 时失败。 +- 从空 PostgreSQL 执行 Initial Schema、启动 API 并通过全量测试。 + +## 实施顺序和退出条件 + +实施顺序固定为: + +1. 3A:只读租户上下文、Query Filter、写入拦截器。 +2. 3B:Host 解析、域名生命周期和 JWT 租户一致性。 +3. 3C:公私内容所有权、分类扩展、题目引用和学习闭环。 +4. 3D:前端运行时配置、发布和缓存。 +5. 3E:重建 Migration、真实 PostgreSQL 隔离测试和全量验证。 + +第三阶段只有在以下条件全部满足后才能结束: + +- 新增普通租户实体即使开发者忘记手写 `TenantId` 条件也不会跨租户读取。 +- API 无法通过 DTO、Host、Header、JWT 或 Attach 伪造其他租户数据。 +- 公共题库和租户私库能够混合完成组卷、答题、收藏和错题闭环。 +- 题目发布新版本不会改变进行中会话或历史答案。 +- 租户自定义域名能够安全获得已发布的前端运行时配置。