307 lines
15 KiB
C#
307 lines
15 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using HelpDesk.Models.Entities;
|
|
|
|
namespace HelpDesk.Data;
|
|
|
|
public class HelpDeskDbContext : DbContext
|
|
{
|
|
public HelpDeskDbContext(DbContextOptions<HelpDeskDbContext> options) : base(options)
|
|
{
|
|
}
|
|
|
|
public DbSet<User> Users => Set<User>();
|
|
public DbSet<Ticket> Tickets => Set<Ticket>();
|
|
public DbSet<TicketComment> TicketComments => Set<TicketComment>();
|
|
public DbSet<Group> Groups => Set<Group>();
|
|
public DbSet<SLAPolicy> SLAPolicies => Set<SLAPolicy>();
|
|
public DbSet<KnowledgeBaseArticle> KnowledgeBaseArticles => Set<KnowledgeBaseArticle>();
|
|
public DbSet<KnowledgeBaseCategory> KnowledgeBaseCategories => Set<KnowledgeBaseCategory>();
|
|
public DbSet<ActivityLog> ActivityLogs => Set<ActivityLog>();
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
base.OnModelCreating(modelBuilder);
|
|
|
|
// ── User ──────────────────────────────────────────────
|
|
modelBuilder.Entity<User>(entity =>
|
|
{
|
|
entity.ToTable("users");
|
|
entity.HasKey(e => e.Id);
|
|
entity.Property(e => e.Id).HasColumnName("id");
|
|
entity.Property(e => e.FirstName).HasColumnName("first_name").HasMaxLength(100).IsRequired();
|
|
entity.Property(e => e.LastName).HasColumnName("last_name").HasMaxLength(100).IsRequired();
|
|
entity.Property(e => e.Email).HasColumnName("email").HasMaxLength(256).IsRequired();
|
|
entity.Property(e => e.PasswordHash).HasColumnName("password_hash").HasMaxLength(500).IsRequired();
|
|
entity.Property(e => e.Role).HasColumnName("role").HasMaxLength(50).IsRequired();
|
|
entity.Property(e => e.IsActive).HasColumnName("is_active");
|
|
entity.Property(e => e.PhoneNumber).HasColumnName("phone_number").HasMaxLength(50);
|
|
entity.Property(e => e.AvatarUrl).HasColumnName("avatar_url").HasMaxLength(500);
|
|
entity.Property(e => e.CreatedAt).HasColumnName("created_at");
|
|
entity.Property(e => e.UpdatedAt).HasColumnName("updated_at");
|
|
|
|
entity.HasIndex(e => e.Email).IsUnique().HasDatabaseName("ix_users_email");
|
|
});
|
|
|
|
// ── Ticket ────────────────────────────────────────────
|
|
modelBuilder.Entity<Ticket>(entity =>
|
|
{
|
|
entity.ToTable("tickets");
|
|
entity.HasKey(e => e.Id);
|
|
entity.Property(e => e.Id).HasColumnName("id");
|
|
entity.Property(e => e.Title).HasColumnName("title").HasMaxLength(200).IsRequired();
|
|
entity.Property(e => e.Description).HasColumnName("description").IsRequired();
|
|
entity.Property(e => e.Status).HasColumnName("status").HasMaxLength(50).IsRequired();
|
|
entity.Property(e => e.Priority).HasColumnName("priority").HasMaxLength(50).IsRequired();
|
|
entity.Property(e => e.TicketNumber).HasColumnName("ticket_number").HasMaxLength(50).IsRequired();
|
|
entity.Property(e => e.RequesterId).HasColumnName("requester_id");
|
|
entity.Property(e => e.AssigneeId).HasColumnName("assignee_id");
|
|
entity.Property(e => e.GroupId).HasColumnName("group_id");
|
|
entity.Property(e => e.SlaPolicyId).HasColumnName("sla_policy_id");
|
|
entity.Property(e => e.CreatedAt).HasColumnName("created_at");
|
|
entity.Property(e => e.UpdatedAt).HasColumnName("updated_at");
|
|
entity.Property(e => e.ResolvedAt).HasColumnName("resolved_at");
|
|
entity.Property(e => e.ClosedAt).HasColumnName("closed_at");
|
|
|
|
entity.HasIndex(e => e.TicketNumber).IsUnique().HasDatabaseName("ix_tickets_ticket_number");
|
|
|
|
// Cascade delete: Ticket -> TicketComment
|
|
entity.HasMany(e => e.Comments)
|
|
.WithOne(c => c.Ticket)
|
|
.HasForeignKey(c => c.TicketId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
// Restrict: User FK references (Requester)
|
|
entity.HasOne(e => e.Requester)
|
|
.WithMany(u => u.RequestedTickets)
|
|
.HasForeignKey(e => e.RequesterId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
// Restrict: User FK references (Assignee)
|
|
entity.HasOne(e => e.Assignee)
|
|
.WithMany(u => u.AssignedTickets)
|
|
.HasForeignKey(e => e.AssigneeId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
entity.HasOne(e => e.Group)
|
|
.WithMany(g => g.Tickets)
|
|
.HasForeignKey(e => e.GroupId)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
|
|
entity.HasOne(e => e.SlaPolicy)
|
|
.WithMany(s => s.Tickets)
|
|
.HasForeignKey(e => e.SlaPolicyId)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
});
|
|
|
|
// ── TicketComment ─────────────────────────────────────
|
|
modelBuilder.Entity<TicketComment>(entity =>
|
|
{
|
|
entity.ToTable("ticket_comments");
|
|
entity.HasKey(e => e.Id);
|
|
entity.Property(e => e.Id).HasColumnName("id");
|
|
entity.Property(e => e.TicketId).HasColumnName("ticket_id");
|
|
entity.Property(e => e.UserId).HasColumnName("user_id");
|
|
entity.Property(e => e.Content).HasColumnName("content").IsRequired();
|
|
entity.Property(e => e.IsInternal).HasColumnName("is_internal");
|
|
entity.Property(e => e.CreatedAt).HasColumnName("created_at");
|
|
|
|
// Restrict: User FK reference
|
|
entity.HasOne(e => e.User)
|
|
.WithMany(u => u.Comments)
|
|
.HasForeignKey(e => e.UserId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
});
|
|
|
|
// ── Group ─────────────────────────────────────────────
|
|
modelBuilder.Entity<Group>(entity =>
|
|
{
|
|
entity.ToTable("groups");
|
|
entity.HasKey(e => e.Id);
|
|
entity.Property(e => e.Id).HasColumnName("id");
|
|
entity.Property(e => e.Name).HasColumnName("name").HasMaxLength(100).IsRequired();
|
|
entity.Property(e => e.Description).HasColumnName("description").HasMaxLength(500);
|
|
entity.Property(e => e.CreatedAt).HasColumnName("created_at");
|
|
});
|
|
|
|
// ── SLAPolicy ─────────────────────────────────────────
|
|
modelBuilder.Entity<SLAPolicy>(entity =>
|
|
{
|
|
entity.ToTable("sla_policies");
|
|
entity.HasKey(e => e.Id);
|
|
entity.Property(e => e.Id).HasColumnName("id");
|
|
entity.Property(e => e.Name).HasColumnName("name").HasMaxLength(100).IsRequired();
|
|
entity.Property(e => e.Description).HasColumnName("description").HasMaxLength(500);
|
|
entity.Property(e => e.ResponseTimeHours).HasColumnName("response_time_hours");
|
|
entity.Property(e => e.ResolutionTimeHours).HasColumnName("resolution_time_hours");
|
|
entity.Property(e => e.IsActive).HasColumnName("is_active");
|
|
entity.Property(e => e.CreatedAt).HasColumnName("created_at");
|
|
});
|
|
|
|
// ── KnowledgeBaseArticle ──────────────────────────────
|
|
modelBuilder.Entity<KnowledgeBaseArticle>(entity =>
|
|
{
|
|
entity.ToTable("knowledge_base_articles");
|
|
entity.HasKey(e => e.Id);
|
|
entity.Property(e => e.Id).HasColumnName("id");
|
|
entity.Property(e => e.Title).HasColumnName("title").HasMaxLength(200).IsRequired();
|
|
entity.Property(e => e.Content).HasColumnName("content").IsRequired();
|
|
entity.Property(e => e.CategoryId).HasColumnName("category_id");
|
|
entity.Property(e => e.AuthorId).HasColumnName("author_id");
|
|
entity.Property(e => e.IsPublished).HasColumnName("is_published");
|
|
entity.Property(e => e.ViewCount).HasColumnName("view_count");
|
|
entity.Property(e => e.CreatedAt).HasColumnName("created_at");
|
|
entity.Property(e => e.UpdatedAt).HasColumnName("updated_at");
|
|
|
|
entity.HasOne(e => e.Category)
|
|
.WithMany(c => c.Articles)
|
|
.HasForeignKey(e => e.CategoryId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
entity.HasOne(e => e.Author)
|
|
.WithMany(u => u.Articles)
|
|
.HasForeignKey(e => e.AuthorId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
});
|
|
|
|
// ── KnowledgeBaseCategory ─────────────────────────────
|
|
modelBuilder.Entity<KnowledgeBaseCategory>(entity =>
|
|
{
|
|
entity.ToTable("knowledge_base_categories");
|
|
entity.HasKey(e => e.Id);
|
|
entity.Property(e => e.Id).HasColumnName("id");
|
|
entity.Property(e => e.Name).HasColumnName("name").HasMaxLength(100).IsRequired();
|
|
entity.Property(e => e.Description).HasColumnName("description").HasMaxLength(500);
|
|
entity.Property(e => e.SortOrder).HasColumnName("sort_order");
|
|
entity.Property(e => e.CreatedAt).HasColumnName("created_at");
|
|
});
|
|
|
|
// ── ActivityLog ───────────────────────────────────────
|
|
modelBuilder.Entity<ActivityLog>(entity =>
|
|
{
|
|
entity.ToTable("activity_logs");
|
|
entity.HasKey(e => e.Id);
|
|
entity.Property(e => e.Id).HasColumnName("id");
|
|
entity.Property(e => e.UserId).HasColumnName("user_id");
|
|
entity.Property(e => e.EntityType).HasColumnName("entity_type").HasMaxLength(100).IsRequired();
|
|
entity.Property(e => e.EntityId).HasColumnName("entity_id");
|
|
entity.Property(e => e.Action).HasColumnName("action").HasMaxLength(100).IsRequired();
|
|
entity.Property(e => e.Details).HasColumnName("details");
|
|
entity.Property(e => e.CreatedAt).HasColumnName("created_at");
|
|
|
|
entity.HasOne(e => e.User)
|
|
.WithMany(u => u.ActivityLogs)
|
|
.HasForeignKey(e => e.UserId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
});
|
|
|
|
// ── Seed Data ─────────────────────────────────────────
|
|
SeedData(modelBuilder);
|
|
}
|
|
|
|
private static void SeedData(ModelBuilder modelBuilder)
|
|
{
|
|
var adminId = Guid.Parse("a0000000-0000-0000-0000-000000000001");
|
|
var generalGroupId = Guid.Parse("b0000000-0000-0000-0000-000000000001");
|
|
var ventasGroupId = Guid.Parse("b0000000-0000-0000-0000-000000000002");
|
|
var desarrolloGroupId = Guid.Parse("b0000000-0000-0000-0000-000000000003");
|
|
var standardSlaId = Guid.Parse("c0000000-0000-0000-0000-000000000001");
|
|
var premiumSlaId = Guid.Parse("c0000000-0000-0000-0000-000000000002");
|
|
var generalCategoryId = Guid.Parse("d0000000-0000-0000-0000-000000000001");
|
|
var faqCategoryId = Guid.Parse("d0000000-0000-0000-0000-000000000002");
|
|
var tutorialsCategoryId = Guid.Parse("d0000000-0000-0000-0000-000000000003");
|
|
|
|
// SuperAdmin user
|
|
modelBuilder.Entity<User>().HasData(new User
|
|
{
|
|
Id = adminId,
|
|
FirstName = "Super",
|
|
LastName = "Admin",
|
|
Email = "admin@helpdesk.local",
|
|
// BCrypt hash for "Admin123!"
|
|
PasswordHash = "$2a$11$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy",
|
|
Role = HelpDesk.Models.Enums.UserRole.SuperAdmin,
|
|
IsActive = true,
|
|
PhoneNumber = "+1-555-0100",
|
|
CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc),
|
|
UpdatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)
|
|
});
|
|
|
|
// Default groups
|
|
modelBuilder.Entity<Group>().HasData(
|
|
new Group
|
|
{
|
|
Id = generalGroupId,
|
|
Name = "Soporte General",
|
|
Description = "Grupo de soporte técnico general",
|
|
CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)
|
|
},
|
|
new Group
|
|
{
|
|
Id = ventasGroupId,
|
|
Name = "Ventas",
|
|
Description = "Grupo de soporte de ventas",
|
|
CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)
|
|
},
|
|
new Group
|
|
{
|
|
Id = desarrolloGroupId,
|
|
Name = "Desarrollo",
|
|
Description = "Grupo de desarrollo de software",
|
|
CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)
|
|
}
|
|
);
|
|
|
|
// SLA policies
|
|
modelBuilder.Entity<SLAPolicy>().HasData(
|
|
new SLAPolicy
|
|
{
|
|
Id = standardSlaId,
|
|
Name = "Estándar",
|
|
Description = "Política estándar: respuesta en 4 horas, resolución en 24 horas",
|
|
ResponseTimeHours = 4,
|
|
ResolutionTimeHours = 24,
|
|
IsActive = true,
|
|
CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)
|
|
},
|
|
new SLAPolicy
|
|
{
|
|
Id = premiumSlaId,
|
|
Name = "Premium",
|
|
Description = "Política premium: respuesta en 1 hora, resolución en 8 horas",
|
|
ResponseTimeHours = 1,
|
|
ResolutionTimeHours = 8,
|
|
IsActive = true,
|
|
CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)
|
|
}
|
|
);
|
|
|
|
// Knowledge base categories
|
|
modelBuilder.Entity<KnowledgeBaseCategory>().HasData(
|
|
new KnowledgeBaseCategory
|
|
{
|
|
Id = generalCategoryId,
|
|
Name = "General",
|
|
Description = "Artículos generales de ayuda",
|
|
SortOrder = 1,
|
|
CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)
|
|
},
|
|
new KnowledgeBaseCategory
|
|
{
|
|
Id = faqCategoryId,
|
|
Name = "Preguntas Frecuentes",
|
|
Description = "Preguntas frecuentes y respuestas",
|
|
SortOrder = 2,
|
|
CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)
|
|
},
|
|
new KnowledgeBaseCategory
|
|
{
|
|
Id = tutorialsCategoryId,
|
|
Name = "Tutoriales",
|
|
Description = "Guías y tutoriales paso a paso",
|
|
SortOrder = 3,
|
|
CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)
|
|
}
|
|
);
|
|
}
|
|
}
|