Record create/update/delete events across novel content (chapters, beats, characters, arc stages, tags, locations, questions) into an append-only ActivityEvent log, aggregate by UTC day, and surface as a heatmap on the novels list and each novel's dashboard. Backfills history from existing CreatedAt timestamps on first boot after the migration.
71 lines
3.0 KiB
C#
71 lines
3.0 KiB
C#
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Novelly.Api.Activity;
|
|
using Novelly.Api.Agent;
|
|
using Novelly.Api.Beats;
|
|
using Novelly.Api.Chapters;
|
|
using Novelly.Api.Characters;
|
|
using Novelly.Api.Genres;
|
|
using Novelly.Api.Imports;
|
|
using Novelly.Api.Locations;
|
|
using Novelly.Api.Novels;
|
|
using Novelly.Api.Questions;
|
|
using Novelly.Api.Tags;
|
|
using Novelly.Api.Users;
|
|
|
|
namespace Novelly.Api.Data;
|
|
|
|
internal class UtcTicksConverter() : ValueConverter<DateTimeOffset, long>(value => value.UtcTicks, ticks => new DateTimeOffset(ticks, TimeSpan.Zero));
|
|
|
|
public class NovelDbContext(DbContextOptions<NovelDbContext> options) : IdentityUserContext<NovellyUser, Guid>(options), INovelDbContext
|
|
{
|
|
public DbSet<Novel> Novels => Set<Novel>();
|
|
public DbSet<Character> Characters => Set<Character>();
|
|
public DbSet<CharacterRelationship> CharacterRelationships => Set<CharacterRelationship>();
|
|
public DbSet<CharacterArcStage> CharacterArcStages => Set<CharacterArcStage>();
|
|
public DbSet<Beat> Beats => Set<Beat>();
|
|
public DbSet<Tag> Tags => Set<Tag>();
|
|
public DbSet<Location> Locations => Set<Location>();
|
|
public DbSet<Chapter> Chapters => Set<Chapter>();
|
|
public DbSet<OpenQuestion> OpenQuestions => Set<OpenQuestion>();
|
|
public DbSet<AgentConversation> Conversations => Set<AgentConversation>();
|
|
public DbSet<AgentMessage> AgentMessages => Set<AgentMessage>();
|
|
public DbSet<ImportJob> ImportJobs => Set<ImportJob>();
|
|
public DbSet<Genre> Genres => Set<Genre>();
|
|
public DbSet<NovelMember> NovelMembers => Set<NovelMember>();
|
|
public DbSet<ActivityEvent> ActivityEvents => Set<ActivityEvent>();
|
|
|
|
Task<int> INovelDbContext.SaveChangesAsync(CancellationToken cancellationToken) => base.SaveChangesAsync(cancellationToken);
|
|
|
|
protected override void ConfigureConventions(ModelConfigurationBuilder builder) => builder.Properties<DateTimeOffset>().HaveConversion<UtcTicksConverter>();
|
|
|
|
protected override void OnModelCreating(ModelBuilder builder)
|
|
{
|
|
base.OnModelCreating(builder);
|
|
builder.ApplyConfigurationsFromAssembly(typeof(NovelDbContext).Assembly);
|
|
}
|
|
}
|
|
|
|
public interface INovelDbContext
|
|
{
|
|
DbSet<Novel> Novels { get; }
|
|
DbSet<Character> Characters { get; }
|
|
DbSet<CharacterRelationship> CharacterRelationships { get; }
|
|
DbSet<CharacterArcStage> CharacterArcStages { get; }
|
|
DbSet<Beat> Beats { get; }
|
|
DbSet<Tag> Tags { get; }
|
|
DbSet<Location> Locations { get; }
|
|
DbSet<Chapter> Chapters { get; }
|
|
DbSet<OpenQuestion> OpenQuestions { get; }
|
|
DbSet<AgentConversation> Conversations { get; }
|
|
DbSet<AgentMessage> AgentMessages { get; }
|
|
DbSet<ImportJob> ImportJobs { get; }
|
|
DbSet<Genre> Genres { get; }
|
|
DbSet<NovellyUser> Users { get; }
|
|
DbSet<NovelMember> NovelMembers { get; }
|
|
DbSet<ActivityEvent> ActivityEvents { get; }
|
|
|
|
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
|
}
|