Implemented a basic ticket system
This commit is contained in:
@@ -59,6 +59,15 @@ public class ConfigV1
|
||||
[JsonProperty("Sentry")] public SentryData Sentry { get; set; } = new();
|
||||
|
||||
[JsonProperty("Stripe")] public StripeData Stripe { get; set; } = new();
|
||||
|
||||
[JsonProperty("Tickets")] public TicketsData Tickets { get; set; } = new();
|
||||
}
|
||||
|
||||
public class TicketsData
|
||||
{
|
||||
[JsonProperty("WelcomeMessage")]
|
||||
[Description("The message that will be sent when a user created a ticket")]
|
||||
public string WelcomeMessage { get; set; } = "Welcome to the support";
|
||||
}
|
||||
|
||||
public class StripeData
|
||||
|
||||
@@ -44,6 +44,9 @@ public class DataContext : DbContext
|
||||
public DbSet<SecurityLog> SecurityLogs { get; set; }
|
||||
public DbSet<BlocklistIp> BlocklistIps { get; set; }
|
||||
public DbSet<WhitelistIp> WhitelistIps { get; set; }
|
||||
|
||||
public DbSet<Ticket> Tickets { get; set; }
|
||||
public DbSet<TicketMessage> TicketMessages { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
|
||||
19
Moonlight/App/Database/Entities/Ticket.cs
Normal file
19
Moonlight/App/Database/Entities/Ticket.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class Ticket
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string IssueTopic { get; set; } = "";
|
||||
public string IssueDescription { get; set; } = "";
|
||||
public string IssueTries { get; set; } = "";
|
||||
public User CreatedBy { get; set; }
|
||||
public User? AssignedTo { get; set; }
|
||||
public TicketPriority Priority { get; set; }
|
||||
public TicketStatus Status { get; set; }
|
||||
public TicketSubject Subject { get; set; }
|
||||
public int SubjectId { get; set; }
|
||||
public List<TicketMessage> Messages { get; set; } = new();
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
14
Moonlight/App/Database/Entities/TicketMessage.cs
Normal file
14
Moonlight/App/Database/Entities/TicketMessage.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class TicketMessage
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Content { get; set; } = "";
|
||||
public string? AttachmentUrl { get; set; }
|
||||
public User? Sender { get; set; }
|
||||
public bool IsSystemMessage { get; set; }
|
||||
public bool IsEdited { get; set; }
|
||||
public bool IsSupportMessage { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
1233
Moonlight/App/Database/Migrations/20230803012947_AddNewTicketModels.Designer.cs
generated
Normal file
1233
Moonlight/App/Database/Migrations/20230803012947_AddNewTicketModels.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddNewTicketModels : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Tickets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
IssueTopic = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
IssueDescription = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
IssueTries = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
CreatedById = table.Column<int>(type: "int", nullable: false),
|
||||
AssignedToId = table.Column<int>(type: "int", nullable: true),
|
||||
Priority = table.Column<int>(type: "int", nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
Subject = table.Column<int>(type: "int", nullable: false),
|
||||
SubjectId = table.Column<int>(type: "int", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Tickets", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Tickets_Users_AssignedToId",
|
||||
column: x => x.AssignedToId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_Tickets_Users_CreatedById",
|
||||
column: x => x.CreatedById,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TicketMessages",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Content = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
AttachmentUrl = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
SenderId = table.Column<int>(type: "int", nullable: true),
|
||||
IsSystemMessage = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
IsEdited = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
IsSupportMessage = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
TicketId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_TicketMessages", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_TicketMessages_Tickets_TicketId",
|
||||
column: x => x.TicketId,
|
||||
principalTable: "Tickets",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_TicketMessages_Users_SenderId",
|
||||
column: x => x.SenderId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id");
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_TicketMessages_SenderId",
|
||||
table: "TicketMessages",
|
||||
column: "SenderId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_TicketMessages_TicketId",
|
||||
table: "TicketMessages",
|
||||
column: "TicketId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tickets_AssignedToId",
|
||||
table: "Tickets",
|
||||
column: "AssignedToId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tickets_CreatedById",
|
||||
table: "Tickets",
|
||||
column: "CreatedById");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "TicketMessages");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Tickets");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -714,6 +714,97 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.ToTable("SupportChatMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Ticket", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("AssignedToId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("CreatedById")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("IssueDescription")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("IssueTopic")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("IssueTries")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Subject")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SubjectId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AssignedToId");
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.ToTable("Tickets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.TicketMessage", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("AttachmentUrl")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("IsEdited")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsSupportMessage")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsSystemMessage")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int?>("SenderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("TicketId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SenderId");
|
||||
|
||||
b.HasIndex("TicketId");
|
||||
|
||||
b.ToTable("TicketMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1039,6 +1130,36 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.Navigation("Sender");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Ticket", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.User", "AssignedTo")
|
||||
.WithMany()
|
||||
.HasForeignKey("AssignedToId");
|
||||
|
||||
b.HasOne("Moonlight.App.Database.Entities.User", "CreatedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("CreatedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AssignedTo");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.TicketMessage", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.User", "Sender")
|
||||
.WithMany()
|
||||
.HasForeignKey("SenderId");
|
||||
|
||||
b.HasOne("Moonlight.App.Database.Entities.Ticket", null)
|
||||
.WithMany("Messages")
|
||||
.HasForeignKey("TicketId");
|
||||
|
||||
b.Navigation("Sender");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.Subscription", "CurrentSubscription")
|
||||
@@ -1094,6 +1215,11 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.Navigation("Variables");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Ticket", b =>
|
||||
{
|
||||
b.Navigation("Messages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b =>
|
||||
{
|
||||
b.Navigation("Databases");
|
||||
|
||||
21
Moonlight/App/Models/Forms/CreateTicketDataModel.cs
Normal file
21
Moonlight/App/Models/Forms/CreateTicketDataModel.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Models.Forms;
|
||||
|
||||
public class CreateTicketDataModel
|
||||
{
|
||||
[Required(ErrorMessage = "You need to specify a issue topic")]
|
||||
[MinLength(5, ErrorMessage = "The issue topic needs to be longer than 5 characters")]
|
||||
public string IssueTopic { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "You need to specify a issue description")]
|
||||
[MinLength(10, ErrorMessage = "The issue description needs to be longer than 10 characters")]
|
||||
public string IssueDescription { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "You need to specify your tries to solve this issue")]
|
||||
public string IssueTries { get; set; }
|
||||
|
||||
public TicketSubject Subject { get; set; }
|
||||
public int SubjectId { get; set; }
|
||||
}
|
||||
9
Moonlight/App/Models/Misc/TicketPriority.cs
Normal file
9
Moonlight/App/Models/Misc/TicketPriority.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum TicketPriority
|
||||
{
|
||||
Low = 0,
|
||||
Medium = 1,
|
||||
High = 2,
|
||||
Critical = 3
|
||||
}
|
||||
9
Moonlight/App/Models/Misc/TicketStatus.cs
Normal file
9
Moonlight/App/Models/Misc/TicketStatus.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum TicketStatus
|
||||
{
|
||||
Closed = 0,
|
||||
Open = 1,
|
||||
WaitingForUser = 2,
|
||||
Pending = 3
|
||||
}
|
||||
9
Moonlight/App/Models/Misc/TicketSubject.cs
Normal file
9
Moonlight/App/Models/Misc/TicketSubject.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum TicketSubject
|
||||
{
|
||||
Webspace = 0,
|
||||
Server = 1,
|
||||
Domain = 2,
|
||||
Other = 3
|
||||
}
|
||||
95
Moonlight/App/Services/Tickets/TicketAdminService.cs
Normal file
95
Moonlight/App/Services/Tickets/TicketAdminService.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Models.Misc;
|
||||
using Moonlight.App.Services.Files;
|
||||
using Moonlight.App.Services.Sessions;
|
||||
|
||||
namespace Moonlight.App.Services.Tickets;
|
||||
|
||||
public class TicketAdminService
|
||||
{
|
||||
private readonly TicketServerService TicketServerService;
|
||||
private readonly IdentityService IdentityService;
|
||||
private readonly BucketService BucketService;
|
||||
|
||||
public Ticket? Ticket { get; set; }
|
||||
|
||||
public TicketAdminService(
|
||||
TicketServerService ticketServerService,
|
||||
IdentityService identityService,
|
||||
BucketService bucketService)
|
||||
{
|
||||
TicketServerService = ticketServerService;
|
||||
IdentityService = identityService;
|
||||
BucketService = bucketService;
|
||||
}
|
||||
|
||||
public async Task<Dictionary<Ticket, TicketMessage?>> GetAssigned()
|
||||
{
|
||||
return await TicketServerService.GetUserAssignedTickets(IdentityService.User);
|
||||
}
|
||||
|
||||
public async Task<Dictionary<Ticket, TicketMessage?>> GetUnAssigned()
|
||||
{
|
||||
return await TicketServerService.GetUnAssignedTickets();
|
||||
}
|
||||
|
||||
public async Task<Ticket> Create(string issueTopic, string issueDescription, string issueTries,
|
||||
TicketSubject subject, int subjectId)
|
||||
{
|
||||
return await TicketServerService.Create(
|
||||
IdentityService.User,
|
||||
issueTopic,
|
||||
issueDescription,
|
||||
issueTries,
|
||||
subject,
|
||||
subjectId
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<TicketMessage> Send(string content, IBrowserFile? file = null)
|
||||
{
|
||||
string? attachment = null;
|
||||
|
||||
if (file != null)
|
||||
{
|
||||
attachment = await BucketService.StoreFile(
|
||||
"tickets",
|
||||
file.OpenReadStream(1024 * 1024 * 5),
|
||||
file.Name);
|
||||
}
|
||||
|
||||
return await TicketServerService.SendMessage(
|
||||
Ticket!,
|
||||
IdentityService.User,
|
||||
content,
|
||||
attachment,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public async Task UpdateStatus(TicketStatus status)
|
||||
{
|
||||
await TicketServerService.UpdateStatus(Ticket!, status);
|
||||
}
|
||||
|
||||
public async Task UpdatePriority(TicketPriority priority)
|
||||
{
|
||||
await TicketServerService.UpdatePriority(Ticket!, priority);
|
||||
}
|
||||
|
||||
public async Task<TicketMessage[]> GetMessages()
|
||||
{
|
||||
return await TicketServerService.GetMessages(Ticket!);
|
||||
}
|
||||
|
||||
public async Task Claim()
|
||||
{
|
||||
await TicketServerService.Claim(Ticket!, IdentityService.User);
|
||||
}
|
||||
|
||||
public async Task UnClaim()
|
||||
{
|
||||
await TicketServerService.Claim(Ticket!);
|
||||
}
|
||||
}
|
||||
68
Moonlight/App/Services/Tickets/TicketClientService.cs
Normal file
68
Moonlight/App/Services/Tickets/TicketClientService.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Models.Misc;
|
||||
using Moonlight.App.Services.Files;
|
||||
using Moonlight.App.Services.Sessions;
|
||||
|
||||
namespace Moonlight.App.Services.Tickets;
|
||||
|
||||
public class TicketClientService
|
||||
{
|
||||
private readonly TicketServerService TicketServerService;
|
||||
private readonly IdentityService IdentityService;
|
||||
private readonly BucketService BucketService;
|
||||
|
||||
public Ticket? Ticket { get; set; }
|
||||
|
||||
public TicketClientService(
|
||||
TicketServerService ticketServerService,
|
||||
IdentityService identityService,
|
||||
BucketService bucketService)
|
||||
{
|
||||
TicketServerService = ticketServerService;
|
||||
IdentityService = identityService;
|
||||
BucketService = bucketService;
|
||||
}
|
||||
|
||||
public async Task<Dictionary<Ticket, TicketMessage?>> Get()
|
||||
{
|
||||
return await TicketServerService.GetUserTickets(IdentityService.User);
|
||||
}
|
||||
|
||||
public async Task<Ticket> Create(string issueTopic, string issueDescription, string issueTries, TicketSubject subject, int subjectId)
|
||||
{
|
||||
return await TicketServerService.Create(
|
||||
IdentityService.User,
|
||||
issueTopic,
|
||||
issueDescription,
|
||||
issueTries,
|
||||
subject,
|
||||
subjectId
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<TicketMessage> Send(string content, IBrowserFile? file = null)
|
||||
{
|
||||
string? attachment = null;
|
||||
|
||||
if (file != null)
|
||||
{
|
||||
attachment = await BucketService.StoreFile(
|
||||
"tickets",
|
||||
file.OpenReadStream(1024 * 1024 * 5),
|
||||
file.Name);
|
||||
}
|
||||
|
||||
return await TicketServerService.SendMessage(
|
||||
Ticket!,
|
||||
IdentityService.User,
|
||||
content,
|
||||
attachment
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<TicketMessage[]> GetMessages()
|
||||
{
|
||||
return await TicketServerService.GetMessages(Ticket!);
|
||||
}
|
||||
}
|
||||
249
Moonlight/App/Services/Tickets/TicketServerService.cs
Normal file
249
Moonlight/App/Services/Tickets/TicketServerService.cs
Normal file
@@ -0,0 +1,249 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Events;
|
||||
using Moonlight.App.Models.Misc;
|
||||
using Moonlight.App.Repositories;
|
||||
|
||||
namespace Moonlight.App.Services.Tickets;
|
||||
|
||||
public class TicketServerService
|
||||
{
|
||||
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||
private readonly EventSystem Event;
|
||||
private readonly ConfigService ConfigService;
|
||||
|
||||
public TicketServerService(
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
EventSystem eventSystem,
|
||||
ConfigService configService)
|
||||
{
|
||||
ServiceScopeFactory = serviceScopeFactory;
|
||||
Event = eventSystem;
|
||||
ConfigService = configService;
|
||||
}
|
||||
|
||||
public async Task<Ticket> Create(User creator, string issueTopic, string issueDescription, string issueTries, TicketSubject subject, int subjectId)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
|
||||
var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
|
||||
|
||||
var creatorUser = userRepo
|
||||
.Get()
|
||||
.First(x => x.Id == creator.Id);
|
||||
|
||||
var ticket = ticketRepo.Add(new()
|
||||
{
|
||||
Priority = TicketPriority.Low,
|
||||
Status = TicketStatus.Open,
|
||||
AssignedTo = null,
|
||||
IssueTopic = issueTopic,
|
||||
IssueDescription = issueDescription,
|
||||
IssueTries = issueTries,
|
||||
Subject = subject,
|
||||
SubjectId = subjectId,
|
||||
CreatedBy = creatorUser
|
||||
});
|
||||
|
||||
await Event.Emit("tickets.new", ticket);
|
||||
|
||||
// Do automatic stuff here
|
||||
await SendSystemMessage(ticket, ConfigService.Get().Moonlight.Tickets.WelcomeMessage);
|
||||
//TODO: Check for opening times
|
||||
|
||||
return ticket;
|
||||
}
|
||||
public async Task SendSystemMessage(Ticket t, string content, string? attachmentUrl = null)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
|
||||
|
||||
var ticket = ticketRepo.Get().First(x => x.Id == t.Id);
|
||||
|
||||
var message = new TicketMessage()
|
||||
{
|
||||
Content = content,
|
||||
Sender = null,
|
||||
AttachmentUrl = attachmentUrl,
|
||||
IsSystemMessage = true
|
||||
};
|
||||
|
||||
ticket.Messages.Add(message);
|
||||
ticketRepo.Update(ticket);
|
||||
|
||||
await Event.Emit("tickets.message", message);
|
||||
await Event.Emit($"tickets.{ticket.Id}.message", message);
|
||||
}
|
||||
public async Task UpdatePriority(Ticket t, TicketPriority priority)
|
||||
{
|
||||
if(t.Priority == priority)
|
||||
return;
|
||||
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
|
||||
|
||||
var ticket = ticketRepo.Get().First(x => x.Id == t.Id);
|
||||
|
||||
ticket.Priority = priority;
|
||||
|
||||
ticketRepo.Update(ticket);
|
||||
|
||||
await Event.Emit("tickets.status", ticket);
|
||||
await Event.Emit($"tickets.{ticket.Id}.status", ticket);
|
||||
|
||||
await SendSystemMessage(ticket, $"The ticket priority has been changed to: {priority}");
|
||||
}
|
||||
public async Task UpdateStatus(Ticket t, TicketStatus status)
|
||||
{
|
||||
if(t.Status == status)
|
||||
return;
|
||||
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
|
||||
|
||||
var ticket = ticketRepo.Get().First(x => x.Id == t.Id);
|
||||
|
||||
ticket.Status = status;
|
||||
|
||||
ticketRepo.Update(ticket);
|
||||
|
||||
await Event.Emit("tickets.status", ticket);
|
||||
await Event.Emit($"tickets.{ticket.Id}.status", ticket);
|
||||
|
||||
await SendSystemMessage(ticket, $"The ticket status has been changed to: {status}");
|
||||
}
|
||||
public async Task<TicketMessage> SendMessage(Ticket t, User sender, string content, string? attachmentUrl = null, bool isSupportMessage = false)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
|
||||
var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
|
||||
|
||||
var ticket = ticketRepo.Get().First(x => x.Id == t.Id);
|
||||
var user = userRepo.Get().First(x => x.Id == sender.Id);
|
||||
|
||||
var message = new TicketMessage()
|
||||
{
|
||||
Content = content,
|
||||
Sender = user,
|
||||
AttachmentUrl = attachmentUrl,
|
||||
IsSupportMessage = isSupportMessage
|
||||
};
|
||||
|
||||
ticket.Messages.Add(message);
|
||||
ticketRepo.Update(ticket);
|
||||
|
||||
await Event.Emit("tickets.message", message);
|
||||
await Event.Emit($"tickets.{ticket.Id}.message", message);
|
||||
|
||||
return message;
|
||||
}
|
||||
public Task<Dictionary<Ticket, TicketMessage?>> GetUserTickets(User u)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
|
||||
|
||||
var tickets = ticketRepo
|
||||
.Get()
|
||||
.Include(x => x.CreatedBy)
|
||||
.Include(x => x.Messages)
|
||||
.Where(x => x.CreatedBy.Id == u.Id)
|
||||
.Where(x => x.Status != TicketStatus.Closed)
|
||||
.ToArray();
|
||||
|
||||
var result = new Dictionary<Ticket, TicketMessage?>();
|
||||
|
||||
foreach (var ticket in tickets)
|
||||
{
|
||||
var message = ticket.Messages
|
||||
.OrderByDescending(x => x.Id)
|
||||
.FirstOrDefault();
|
||||
|
||||
result.Add(ticket, message);
|
||||
}
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
public Task<Dictionary<Ticket, TicketMessage?>> GetUserAssignedTickets(User u)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
|
||||
|
||||
var tickets = ticketRepo
|
||||
.Get()
|
||||
.Include(x => x.CreatedBy)
|
||||
.Include(x => x.Messages)
|
||||
.Where(x => x.Status != TicketStatus.Closed)
|
||||
.Where(x => x.AssignedTo.Id == u.Id)
|
||||
.ToArray();
|
||||
|
||||
var result = new Dictionary<Ticket, TicketMessage?>();
|
||||
|
||||
foreach (var ticket in tickets)
|
||||
{
|
||||
var message = ticket.Messages
|
||||
.OrderByDescending(x => x.Id)
|
||||
.FirstOrDefault();
|
||||
|
||||
result.Add(ticket, message);
|
||||
}
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
public Task<Dictionary<Ticket, TicketMessage?>> GetUnAssignedTickets()
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
|
||||
|
||||
var tickets = ticketRepo
|
||||
.Get()
|
||||
.Include(x => x.CreatedBy)
|
||||
.Include(x => x.Messages)
|
||||
.Include(x => x.AssignedTo)
|
||||
.Where(x => x.AssignedTo == null)
|
||||
.Where(x => x.Status != TicketStatus.Closed)
|
||||
.ToArray();
|
||||
|
||||
var result = new Dictionary<Ticket, TicketMessage?>();
|
||||
|
||||
foreach (var ticket in tickets)
|
||||
{
|
||||
var message = ticket.Messages
|
||||
.OrderByDescending(x => x.Id)
|
||||
.FirstOrDefault();
|
||||
|
||||
result.Add(ticket, message);
|
||||
}
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
public Task<TicketMessage[]> GetMessages(Ticket ticket)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
|
||||
|
||||
var tickets = ticketRepo
|
||||
.Get()
|
||||
.Include(x => x.CreatedBy)
|
||||
.Include(x => x.Messages)
|
||||
.First(x => x.Id == ticket.Id);
|
||||
|
||||
return Task.FromResult(tickets.Messages.ToArray());
|
||||
}
|
||||
|
||||
public async Task Claim(Ticket t, User? u = null)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
|
||||
var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
|
||||
|
||||
var ticket = ticketRepo.Get().Include(x => x.AssignedTo).First(x => x.Id == t.Id);
|
||||
var user = u == null ? u : userRepo.Get().First(x => x.Id == u.Id);
|
||||
|
||||
ticket.AssignedTo = user;
|
||||
|
||||
ticketRepo.Update(ticket);
|
||||
|
||||
await Event.Emit("tickets.status", ticket);
|
||||
await Event.Emit($"tickets.{ticket.Id}.status", ticket);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user