feat: normalize subscription architecture, integrate pgvector, and implement Stripe webhook subscription management.

This commit is contained in:
2026-05-05 15:07:48 +02:00
parent e21c24b66d
commit 311eaa8b04
29 changed files with 1699 additions and 199 deletions
+4
View File
@@ -23,6 +23,10 @@ public class Ebook
public string? CoverUrl { get; set; }
[Required]
[MaxLength(128)]
public string TenantId { get; set; } = "global";
public DateTime AddedDate { get; set; } = DateTime.UtcNow;
public DateTime? LastReadDate { get; set; }
@@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using NexusReader.Domain.Enums;
using Pgvector;
namespace NexusReader.Domain.Entities;
@@ -30,8 +31,7 @@ public class KnowledgeUnit
[MaxLength(128)]
public string TenantId { get; set; } = string.Empty;
[Column(TypeName = "vector(768)")] // Default for text-embedding-004
public float[]? Vector { get; set; }
public Vector? Vector { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
+25 -9
View File
@@ -1,34 +1,49 @@
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace NexusReader.Domain.Entities;
/// <summary>
/// Extended Identity user for the Nexus AI E-Reader SaaS platform.
/// </summary>
public class NexusUser : IdentityUser
{
/// <summary>
/// Total number of AI tokens allowed for the current billing period.
/// User's display name or full name.
/// </summary>
[MaxLength(100)]
public string? DisplayName { get; set; }
/// <summary>
/// Total AI tokens available for the user (depends on subscription).
/// </summary>
public int AITokenLimit { get; set; }
/// <summary>
/// Number of AI tokens consumed in the current billing period.
/// AI tokens consumed by the user in the current billing period.
/// </summary>
public int AITokensUsed { get; set; }
/// <summary>
/// Unique identifier for the tenant (SaaS multi-tenancy support).
/// Date when the user last performed an AI-related action.
/// </summary>
public DateTime? LastAiActionDate { get; set; }
/// <summary>
/// Multi-tenant identifier.
/// </summary>
[Required]
[MaxLength(128)]
public string TenantId { get; set; } = "global";
/// <summary>
/// Current subscription plan (e.g., "Free", "Pro", "Enterprise").
/// Foreign key for the current subscription plan.
/// </summary>
public string CurrentPlan { get; set; } = "Free";
[Required]
public int SubscriptionPlanId { get; set; }
/// <summary>
/// Navigation property for the current subscription plan.
/// </summary>
public SubscriptionPlan? SubscriptionPlan { get; set; }
/// <summary>
/// Collection of e-books owned by the user.
@@ -43,10 +58,11 @@ public class NexusUser : IdentityUser
/// <summary>
/// ID of the last page read by the user.
/// </summary>
[MaxLength(255)]
public string? LastReadPageId { get; set; }
/// <summary>
/// Timestamp of the last reading progress update.
/// Last read timestamp.
/// </summary>
public DateTime? LastReadAt { get; set; }
}
@@ -17,6 +17,10 @@ public class QuizResult
[ForeignKey(nameof(UserId))]
public NexusUser? User { get; set; }
[Required]
[MaxLength(128)]
public string TenantId { get; set; } = "global";
[Required]
public string Topic { get; set; } = string.Empty;
@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Pgvector;
namespace NexusReader.Domain.Entities;
@@ -27,8 +28,7 @@ public class SemanticKnowledgeCache
[MaxLength(128)]
public string TenantId { get; set; } = string.Empty;
[Column(TypeName = "vector(1536)")] // text-embedding-004 has 768 or 1536 dims, assuming 1536 for high-fidelity
public float[]? Vector { get; set; }
public Vector? Vector { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
@@ -0,0 +1,30 @@
using System.ComponentModel.DataAnnotations;
namespace NexusReader.Domain.Entities;
public class SubscriptionPlan
{
public const string FreeName = "Free";
public const string BasicName = "Basic";
public const string ProName = "Pro";
public const string EnterpriseName = "Enterprise";
public const int FreeId = 1;
public const int BasicId = 2;
public const int ProId = 3;
public const int EnterpriseId = 4;
[Key]
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string PlanName { get; set; } = string.Empty;
public int AITokenLimit { get; set; }
public decimal MonthlyPrice { get; set; }
[MaxLength(50)]
public string StripeProductId { get; set; } = string.Empty;
}
@@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="10.0.7" />
<PackageReference Include="Pgvector" Version="0.3.2" />
</ItemGroup>
</Project>