feat: add Member/FamilyUnit DbSets, audit interceptor registration, EF migration

Registers AuditSaveChangesInterceptor in DI and wires it into AppDbContext.
Adds Members and FamilyUnits DbSets with full column/index configuration and
applies the AddMemberAndFamilyUnit migration to the ChurchCRM database.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris Chen
2026-05-27 13:52:58 -07:00
parent cd5413125d
commit 34344cbf83
5 changed files with 938 additions and 12 deletions
@@ -0,0 +1,121 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace ROLAC.API.Migrations
{
/// <inheritdoc />
public partial class AddMemberAndFamilyUnit : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "FamilyUnits",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
FamilyName_en = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
FamilyName_zh = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
Notes = table.Column<string>(type: "text", nullable: true),
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
CreatedBy = table.Column<string>(type: "text", nullable: false),
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
UpdatedBy = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_FamilyUnits", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Members",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
FirstName_en = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
LastName_en = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
NickName = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
FirstName_zh = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
LastName_zh = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
Gender = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: true),
DateOfBirth = table.Column<DateOnly>(type: "date", nullable: true),
BaptismDate = table.Column<DateOnly>(type: "date", nullable: true),
BaptismChurch = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
Email = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
PhoneCell = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: true),
PhoneHome = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: true),
Address = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
City = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
State = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
ZipCode = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
Country = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false, defaultValue: "USA"),
PhotoBlobPath = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false, defaultValue: "Member"),
LanguagePreference = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: false, defaultValue: "en"),
JoinDate = table.Column<DateOnly>(type: "date", nullable: true),
Notes = table.Column<string>(type: "text", nullable: true),
FamilyUnitId = table.Column<int>(type: "integer", nullable: true),
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
CreatedBy = table.Column<string>(type: "character varying(450)", maxLength: 450, nullable: false),
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
UpdatedBy = table.Column<string>(type: "character varying(450)", maxLength: 450, nullable: false),
IsDeleted = table.Column<bool>(type: "boolean", nullable: false),
DeletedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
DeletedBy = table.Column<string>(type: "character varying(450)", maxLength: 450, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Members", x => x.Id);
table.ForeignKey(
name: "FK_Members_FamilyUnits_FamilyUnitId",
column: x => x.FamilyUnitId,
principalTable: "FamilyUnits",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateIndex(
name: "IX_AspNetUsers_MemberId",
table: "AspNetUsers",
column: "MemberId",
unique: true,
filter: "\"MemberId\" IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_Members_Email",
table: "Members",
column: "Email",
filter: "\"Email\" IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_Members_FamilyUnitId",
table: "Members",
column: "FamilyUnitId");
migrationBuilder.CreateIndex(
name: "IX_Members_Status",
table: "Members",
column: "Status",
filter: "\"IsDeleted\" = false");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Members");
migrationBuilder.DropTable(
name: "FamilyUnits");
migrationBuilder.DropIndex(
name: "IX_AspNetUsers_MemberId",
table: "AspNetUsers");
}
}
}