3a4c8fbc10
Phase 3, schema layer. Relational children of CareerProfile — the editable master career profile. See docs/architecture/career-profile-model.md. - New entities (Models/CareerEntities.cs): CareerExperience, CareerEducation, CareerSkill, CareerProject, CareerCertification, CareerLanguage. Each carries OwnerUserId (tenant filter), a stable ItemKey (carried from the blob so future CV variants can reference items), and SortOrder. List fields persist as JSON string columns via [NotMapped] accessors — plain TEXT, reconciler-friendly. - CareerProfile gains typed child collections + a LongTailJson column (contact, summary, interests, achievements, orgs, pubs, courses, custom sections, metadata). ProfileJson becomes a derived projection for legacy read paths. - DbContext: DbSets + tenant query filters + ordered indexes; FK/cascade by convention via the typed collections. - Migration hand-edited to add only the 6 new tables + LongTailJson; the scaffolder re-emitted four reconciler-owned tables (AiWorkspaceNotes, CareerProfiles, InterviewPrepNotes, CareerProfileVersions) which were stripped. The regenerated snapshot now includes them, closing the drift. Verified against a copy of the real dev DB: applies cleanly, no data loss. Long tail (achievements/orgs/pubs/courses) starts as JSON; promotable to relational later without a source-of-truth change. Source-of-truth flip stays deferred; the blob is kept as a derived projection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
49 lines
2.5 KiB
C#
49 lines
2.5 KiB
C#
namespace JobTrackerApi.Models;
|
|
|
|
// The Career Workspace's durable source of truth. Bounded to one row per user for now
|
|
// (see career-workspace-implementation-roadmap.md Phase F1) -- ProfileJson mirrors
|
|
// ApplicationUser.ProfileCvStructureJson during the dual-write window and will become
|
|
// authoritative once every read path is migrated (F5).
|
|
public sealed class CareerProfile
|
|
{
|
|
public int Id { get; set; }
|
|
public string OwnerUserId { get; set; } = string.Empty;
|
|
|
|
// Phase 3: ProfileJson is now a DERIVED projection of the relational children + LongTailJson,
|
|
// serialized into the StructuredCvProfile shape and kept in sync on every save so the legacy
|
|
// read paths (CV rendering, tailoring, match-score) keep working unchanged. It is no longer an
|
|
// independently editable source. See docs/architecture/career-profile-model.md §4.
|
|
public string ProfileJson { get; set; } = string.Empty;
|
|
|
|
// The editable long tail that is not worth a relational table: contact, summary, interests,
|
|
// achievements, organisations, publications, courses, custom/other sections, and AI-extraction
|
|
// metadata. A JSON object (CareerLongTail). The relational children (Experience/Education/Skill/
|
|
// Project/Certification/Language) hold the rest.
|
|
public string LongTailJson { get; set; } = string.Empty;
|
|
|
|
public int Version { get; set; }
|
|
public DateTimeOffset CreatedAtUtc { get; set; } = DateTimeOffset.UtcNow;
|
|
public DateTimeOffset UpdatedAtUtc { get; set; } = DateTimeOffset.UtcNow;
|
|
|
|
public List<CareerExperience> Experiences { get; set; } = new();
|
|
public List<CareerEducation> Education { get; set; } = new();
|
|
public List<CareerSkill> Skills { get; set; } = new();
|
|
public List<CareerProject> Projects { get; set; } = new();
|
|
public List<CareerCertification> Certifications { get; set; } = new();
|
|
public List<CareerLanguage> Languages { get; set; } = new();
|
|
}
|
|
|
|
// Append-only history: one row per save, so profile edits are never silently lost.
|
|
public sealed class CareerProfileVersion
|
|
{
|
|
public int Id { get; set; }
|
|
public string OwnerUserId { get; set; } = string.Empty;
|
|
public int CareerProfileId { get; set; }
|
|
public CareerProfile? CareerProfile { get; set; }
|
|
public int Version { get; set; }
|
|
public string ProfileJson { get; set; } = string.Empty;
|
|
// Where this version came from: "upload" | "rebuild" | "improve" | "reprocess" | "parse" | "manual".
|
|
public string Source { get; set; } = string.Empty;
|
|
public DateTimeOffset CreatedAtUtc { get; set; } = DateTimeOffset.UtcNow;
|
|
}
|