Sube carpetas API, API.Tests, Application y Domain del Backend
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
namespace Domain.Constants;
|
||||
|
||||
/// <summary>
|
||||
/// Identifies audit-trail entries created by an automated process rather than an authenticated user.
|
||||
/// </summary>
|
||||
public static class AuditConstants
|
||||
{
|
||||
public const string SystemUser = "System";
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Domain.Constants;
|
||||
|
||||
/// <summary>
|
||||
/// Regulation defaults applied to matches when no explicit score is entered.
|
||||
/// </summary>
|
||||
public static class MatchDefaults
|
||||
{
|
||||
/// <summary>
|
||||
/// Default score awarded to the present team on a walkover.
|
||||
/// </summary>
|
||||
public const int WalkOverWinnerScore = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Score assigned to the absent team on a walkover.
|
||||
/// </summary>
|
||||
public const int WalkOverLoserScore = 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// The point-deduction summary attached to a Position row when a team carries one or more disciplinary deductions in its division.
|
||||
/// </summary>
|
||||
public class AppliedPointDeduction
|
||||
{
|
||||
/// <summary>
|
||||
/// The total table points subtracted from the team, always positive.
|
||||
/// </summary>
|
||||
public required int Points { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The combined disciplinary reasons, joined when a team has several deductions.
|
||||
/// </summary>
|
||||
public required string Reason { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Domain.Enums;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// An immutable audit-trail entry recording a sensitive action, since the shared owner and admin accounts are otherwise indistinguishable.
|
||||
/// </summary>
|
||||
public class AuditLog : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The sensitive action that was performed.
|
||||
/// </summary>
|
||||
public required AuditAction Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Who performed the action — the authenticated caller's email, or AuditConstants.SystemUser for an automated process with no HTTP identity.
|
||||
/// </summary>
|
||||
public required string Actor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The kind of entity the action targeted, or null when the action is not tied to a single entity.
|
||||
/// </summary>
|
||||
public string? TargetType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifier of the targeted entity, when applicable.
|
||||
/// </summary>
|
||||
public string? TargetId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The target's human-readable name captured at write time, since the target may later be renamed or deleted.
|
||||
/// </summary>
|
||||
public string? TargetName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Free-form human-readable context describing the action in detail.
|
||||
/// </summary>
|
||||
public string? Detail { get; set; }
|
||||
|
||||
// Timestamp is EntityBase.DateCreated in UTC.
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Domain.Enums;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Durable, queryable per-backup record, the source of truth for the admin backup listing.
|
||||
/// </summary>
|
||||
public class BackupRecord : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Where the dump is stored, as an object key or file path understood by the IBackupStorage adapter that persisted it.
|
||||
/// </summary>
|
||||
public required string StoragePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Size, in bytes, of the stored dump.
|
||||
/// </summary>
|
||||
public long SizeBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this backup was triggered manually by an Admin or created by the scheduled backup job.
|
||||
/// </summary>
|
||||
public required BackupOrigin Origin { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class BlogPost : EntityBase
|
||||
{
|
||||
public required string Author { get; set; }
|
||||
public required string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique, URL-friendly identifier used in public blog post links, generated once from the title and never changed afterward.
|
||||
/// </summary>
|
||||
public required string Slug { get; set; }
|
||||
|
||||
public int Views { get; set; } = 0;
|
||||
public string? PhotoUrl { get; set; }
|
||||
public required string MarkdownText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Publication state: when false the post is a draft, hidden from public views and visible only to Admin or Owner callers.
|
||||
/// </summary>
|
||||
public bool IsPublished { get; set; } = true;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A club is the stable identity that persists across seasons, unlike a Team, which is a per-season registration record.
|
||||
/// </summary>
|
||||
public class Club : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The club's stable display name.
|
||||
/// </summary>
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique, URL-friendly identifier used in public club links, generated once from the name and never changed afterward.
|
||||
/// </summary>
|
||||
public required string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional club crest or logo URL, independent of any season team logo.
|
||||
/// </summary>
|
||||
public string? LogoUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Every per-season Team that belongs to this club, across every tournament.
|
||||
/// </summary>
|
||||
public virtual ICollection<Team> Teams { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Optional link to the parent institution this club is a squad of, independent of tournament category. Flat, one level deep — a club with its own ChildClubs cannot itself have a ParentClubId.
|
||||
/// </summary>
|
||||
public Guid? ParentClubId { get; set; }
|
||||
public Club? ParentClub { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Other clubs (squads) that link to this club as their parent institution.
|
||||
/// </summary>
|
||||
public virtual ICollection<Club> ChildClubs { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using Domain.Enums;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class Division : EntityBase
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique, URL-friendly identifier used in public division links, generated once from the name and never changed afterward.
|
||||
/// </summary>
|
||||
public required string Slug { get; set; }
|
||||
|
||||
public bool IsFinished { get; set; } = false;
|
||||
|
||||
public required Tournament Tournament { get; set; }
|
||||
|
||||
public Guid TournamentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Competitive gender category of the division, which must match the parent Tournament's Category.
|
||||
/// </summary>
|
||||
public TournamentCategory Category { get; set; } = TournamentCategory.Masculine;
|
||||
|
||||
public virtual required ICollection<Stage> Stages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Marks a division that intentionally draws teams from across every other division in the tournament, rather than a team's single competitive tier.
|
||||
/// </summary>
|
||||
public bool IsCrossDivisionCup { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Points awarded for a win when building this division's standings, configurable per division. Defaults to 2.
|
||||
/// </summary>
|
||||
public int PointsForWin { get; set; } = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Points awarded for a loss when building this division's standings. Defaults to 1.
|
||||
/// </summary>
|
||||
public int PointsForLoss { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// How many teams qualify to the bracket from each internal group of a multi-group cross-division cup. Defaults to 1.
|
||||
/// </summary>
|
||||
public int QualifiersPerGroup { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Maps this division's final standings position ranges to playoff destinations.
|
||||
/// </summary>
|
||||
public virtual ICollection<DivisionPlayoffMapping> PlayoffMappings { get; set; } = [];
|
||||
|
||||
public virtual ICollection<DivisionTeamRegistration> DivisionTeamRegistrations { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Maps a contiguous range of final group-stage standings positions in a division to a playoff destination.
|
||||
/// </summary>
|
||||
public class DivisionPlayoffMapping : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The division whose standings this mapping applies to.
|
||||
/// </summary>
|
||||
public Guid DivisionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The division whose standings this mapping applies to.
|
||||
/// </summary>
|
||||
public Division? Division { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// First, 1-based inclusive standings position in the range.
|
||||
/// </summary>
|
||||
public required int FromPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Last, 1-based inclusive standings position in the range, greater than or equal to FromPosition.
|
||||
/// </summary>
|
||||
public required int ToPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The playoff destination the teams in this range qualify for, matching the BracketName of that cup's elimination stages.
|
||||
/// </summary>
|
||||
public required string Destination { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// The authoritative record that a team is enrolled in a division, independent of any stage placement.
|
||||
/// </summary>
|
||||
public class DivisionTeamRegistration : EntityBase
|
||||
{
|
||||
public required Guid TeamId { get; set; }
|
||||
public Team? Team { get; set; }
|
||||
|
||||
public required Guid DivisionId { get; set; }
|
||||
public Division? Division { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Domain.Constants;
|
||||
|
||||
using System;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all domain entities, centralizing the identifier and the created and updated audit fields shared by every entity.
|
||||
/// </summary>
|
||||
public abstract class EntityBase
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.Empty;
|
||||
|
||||
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Null until the entity is modified after creation.
|
||||
/// </summary>
|
||||
public DateTime? DateUpdated { get; set; }
|
||||
|
||||
public required string CreatedBy { get; set; } = AuditConstants.SystemUser;
|
||||
public string? UpdatedBy { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using Domain.Enums;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class Match : EntityBase
|
||||
{
|
||||
public DateTime MatchDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The 1-based matchday this match belongs to within its stage — the canonical fixture grouping key, not the calendar date.
|
||||
/// </summary>
|
||||
public int? Round { get; set; }
|
||||
|
||||
public required MatchType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique, URL-friendly identifier used in public match links, generated once from the team names and match date and never changed afterward.
|
||||
/// </summary>
|
||||
public required string Slug { get; set; }
|
||||
|
||||
public Team? HomeTeam { get; set; }
|
||||
|
||||
public Guid? HomeTeamId { get; set; }
|
||||
|
||||
public Team? VisitorTeam { get; set; }
|
||||
|
||||
public Guid? VisitorTeamId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Null until the match result is loaded.
|
||||
/// </summary>
|
||||
public int? HomeScore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Null until the match result is loaded.
|
||||
/// </summary>
|
||||
public int? VisitorScore { get; set; }
|
||||
|
||||
public required bool IsFinished { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the match was decided in overtime, purely informational and with no effect on scoring or standings.
|
||||
/// </summary>
|
||||
public bool WentToOvertime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The match's result lifecycle state: Scheduled, Played, Suspended, or WalkOver.
|
||||
/// </summary>
|
||||
public MatchStatus Status { get; set; } = MatchStatus.Scheduled;
|
||||
|
||||
public Team? WinningTeam { get; set; }
|
||||
|
||||
public Guid? WinningTeamId { get; set; }
|
||||
|
||||
public Stage Stage { get; set; } = default!;
|
||||
|
||||
public Guid? VenueId { get; set; }
|
||||
|
||||
public Venue? Venue { get; set; }
|
||||
|
||||
public Guid StageId { get; set; }
|
||||
|
||||
public virtual ICollection<PlayerStatistic> PlayerStatistics { get; set; } = [];
|
||||
|
||||
public virtual ICollection<Scorer> Scorers { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The best-of-N series this game belongs to, when the stage's BestOf is greater than 1, null for single-game rounds.
|
||||
/// </summary>
|
||||
public Guid? SeriesId { get; set; }
|
||||
public MatchSeries? Series { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The 1-based game number within its series, null when the match does not belong to a series.
|
||||
/// </summary>
|
||||
public int? GameNumber { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A best-of-N playoff series between two teams at one bracket round, grouping the individual Match games that make it up.
|
||||
/// </summary>
|
||||
public class MatchSeries : EntityBase
|
||||
{
|
||||
public required Guid StageId { get; set; }
|
||||
public Stage? Stage { get; set; }
|
||||
|
||||
public required Guid HomeTeamId { get; set; }
|
||||
public Team? HomeTeam { get; set; }
|
||||
|
||||
public required Guid VisitorTeamId { get; set; }
|
||||
public Team? VisitorTeam { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of games in this series, copied from the stage at creation time so a later change to Stage.BestOf never retroactively changes it.
|
||||
/// </summary>
|
||||
public required int BestOf { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set once one team has won the majority of the series' games.
|
||||
/// </summary>
|
||||
public Guid? WinningTeamId { get; set; }
|
||||
public Team? WinningTeam { get; set; }
|
||||
|
||||
public virtual ICollection<Match> Matches { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using Domain.Enums;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class Player : EntityBase
|
||||
{
|
||||
public required string FirstName { get; set; }
|
||||
public string? SecondName { get; set; }
|
||||
public required string LastName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique, URL-friendly identifier used in public player links, generated once from the player's full name and never changed afterward.
|
||||
/// </summary>
|
||||
public required string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The single canonical source string every slug is derived from, in the player's raw casing and with no document number.
|
||||
/// </summary>
|
||||
/// <param name="lastName">The player's last name.</param>
|
||||
/// <param name="firstName">The player's first name.</param>
|
||||
/// <param name="secondName">The optional second given name; blank is treated as absent.</param>
|
||||
/// <returns>The space-joined name source, formed by joining last name, first name, and second name with spaces.</returns>
|
||||
public static string BuildSlugSource(string lastName, string firstName, string? secondName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(secondName))
|
||||
{
|
||||
return $"{lastName} {firstName}";
|
||||
}
|
||||
|
||||
return $"{lastName} {firstName} {secondName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computed and not persisted: the canonical slug source for this player.
|
||||
/// </summary>
|
||||
public string SlugSource => BuildSlugSource(LastName, FirstName, SecondName);
|
||||
|
||||
/// <summary>
|
||||
/// Computed and not persisted to the database: display name with the last name upper-cased.
|
||||
/// </summary>
|
||||
public string FullName => BuildSlugSource(LastName.ToUpper(), FirstName, SecondName);
|
||||
|
||||
public required string DocumentNumber { get; set; }
|
||||
public required bool IsSanctioned { get; set; } = false;
|
||||
public string? PhoneNumber { get; set; }
|
||||
public required DateTime BirthDate { get; set; }
|
||||
public required string SocialSecurity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Denormalized convenience pointer to the player's current team, always kept in sync with their latest PlayerTeamRegistration.
|
||||
/// </summary>
|
||||
public required Team Team { get; set; }
|
||||
public Guid TeamId { get; set; }
|
||||
|
||||
public virtual ICollection<Scorer> Scorers { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Every season this player was registered to a team, the source of truth for roster membership.
|
||||
/// </summary>
|
||||
public virtual ICollection<PlayerTeamRegistration> PlayerTeamRegistrations { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Transient and not persisted: the medical-record eligibility status of this player for the season roster currently being viewed.
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public MedicalRecordStatus? MedicalRecordStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Transient and not persisted: whether the season roster currently being viewed set a real, non-legacy stored medical-record file reference.
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public bool HasMedicalRecordFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Transient and not persisted: whether the player is habilitado for the season roster currently being viewed.
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public bool IsHabilitado =>
|
||||
MedicalRecordStatus == Domain.Enums.MedicalRecordStatus.Approved && HasMedicalRecordFile;
|
||||
|
||||
/// <summary>
|
||||
/// Transient and not persisted: the player's jersey number for the season roster currently being viewed.
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public int? JerseyNumber { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Domain.Enums;
|
||||
|
||||
using System;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class PlayerSanction : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The sanction's length expressed in fechas, not calendar days: a value of N means the subject sits out the next N rounds.
|
||||
/// </summary>
|
||||
public required int Duration { get; set; }
|
||||
public required DateTime IssuedDate { get; set; }
|
||||
public required string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What kind of subject this sanction targets: a player, a team, or a staff member. Defaults to Player.
|
||||
/// </summary>
|
||||
public SanctionSubjectType SubjectType { get; set; } = SanctionSubjectType.Player;
|
||||
|
||||
/// <summary>
|
||||
/// The sanctioned player, only set and required when SubjectType is Player.
|
||||
/// </summary>
|
||||
public Player? Player { get; set; }
|
||||
public Guid? PlayerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The sanctioned team, only set when SubjectType is Team.
|
||||
/// </summary>
|
||||
public Team? Team { get; set; }
|
||||
public Guid? TeamId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The sanctioned staff member's name, only set when SubjectType is Staff.
|
||||
/// </summary>
|
||||
public string? StaffName { get; set; }
|
||||
|
||||
public required Match Match { get; set; }
|
||||
public Guid MatchId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique, URL-friendly identifier used in public and admin sanction links, generated once from the subject's name and the issued date.
|
||||
/// </summary>
|
||||
public required string Slug { get; set; }
|
||||
|
||||
public SanctionAppealStatus AppealStatus { get; set; } = SanctionAppealStatus.None;
|
||||
public string? AppealReason { get; set; }
|
||||
public DateTime? AppealDate { get; set; }
|
||||
public string? AppealResolution { get; set; }
|
||||
public DateTime? AppealResolvedDate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Domain.Enums;
|
||||
|
||||
using System;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class PlayerStatistic : EntityBase
|
||||
{
|
||||
public required int Value { get; set; }
|
||||
public StatisticType Type { get; set; } = StatisticType.Points;
|
||||
public Match? Match { get; set; }
|
||||
public required Guid MatchId { get; set; }
|
||||
public Player? Player { get; set; }
|
||||
public required Guid PlayerId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using Domain.Enums;
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Links a Player to a Team for exactly one season, the source of truth for roster membership, unlike the denormalized Player.TeamId pointer.
|
||||
/// </summary>
|
||||
public class PlayerTeamRegistration : EntityBase
|
||||
{
|
||||
public required Guid PlayerId { get; set; }
|
||||
public Player? Player { get; set; }
|
||||
|
||||
public required Guid TeamId { get; set; }
|
||||
public Team? Team { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The season this registration belongs to, captured at registration time from the Team's TournamentId at that moment.
|
||||
/// </summary>
|
||||
public required Guid TournamentId { get; set; }
|
||||
public Tournament? Tournament { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The player's jersey number for this season registration, null when no number has been assigned yet.
|
||||
/// </summary>
|
||||
public int? JerseyNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Medical-record eligibility status for this season registration, defaulting to Pending.
|
||||
/// </summary>
|
||||
public MedicalRecordStatus MedicalRecordStatus { get; set; } = MedicalRecordStatus.Pending;
|
||||
|
||||
/// <summary>
|
||||
/// Storage reference of the uploaded medical-record file for this season, null until a file is uploaded.
|
||||
/// </summary>
|
||||
public string? MedicalRecordFileUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Original file name of the uploaded medical-record file, kept for display.
|
||||
/// </summary>
|
||||
public string? MedicalRecordFileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional reason recorded when the medical record is rejected.
|
||||
/// </summary>
|
||||
public string? MedicalRecordReviewReason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When the medical record was last approved or rejected, null while still Pending.
|
||||
/// </summary>
|
||||
public DateTime? MedicalRecordReviewedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Refs written before the private-bucket relocation lived under this prefix inside public-images and no longer resolve.
|
||||
/// </summary>
|
||||
public const string LegacyReferencePrefix = "medical-records/";
|
||||
|
||||
/// <summary>
|
||||
/// Whether fileReference is a real, resolvable stored file reference: non-null, non-whitespace, and not a legacy prefix ref.
|
||||
/// </summary>
|
||||
public static bool IsStoredReference(string? fileReference) =>
|
||||
!string.IsNullOrWhiteSpace(fileReference)
|
||||
&& !fileReference.StartsWith(LegacyReferencePrefix, StringComparison.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Whether this registration's player is habilitado for this season: Approved status and a real stored file reference.
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public bool IsHabilitado =>
|
||||
MedicalRecordStatus == Enums.MedicalRecordStatus.Approved && IsStoredReference(MedicalRecordFileUrl);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Domain.Enums;
|
||||
|
||||
using System;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the position of a team in a division, including its match statistics, used in the service layer.
|
||||
/// </summary>
|
||||
public class Position
|
||||
{
|
||||
public required Guid TeamId { get; set; }
|
||||
|
||||
public required string TeamName { get; set; }
|
||||
|
||||
public required string LogoUrl { get; set; }
|
||||
|
||||
public required int MatchesPlayed { get; set; }
|
||||
|
||||
public required int Wins { get; set; }
|
||||
|
||||
public required int Losses { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Basketball score totaled across all matches, distinct from the standings score in Points.
|
||||
/// </summary>
|
||||
public required int PointsFor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Basketball score conceded, totaled across all matches, the counterpart to PointsFor.
|
||||
/// </summary>
|
||||
public required int PointsAgainst { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The point difference, PointsFor minus PointsAgainst, for the team.
|
||||
/// </summary>
|
||||
public required int PointsDifference { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The total table points accumulated by the team, calculated from the division's points-per-win and points-per-loss configuration.
|
||||
/// </summary>
|
||||
public required int Points { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The tiebreaker criterion that separated this team from the team ranked immediately above it.
|
||||
/// </summary>
|
||||
public TiebreakerCriterion? ResolvedBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The disciplinary point deduction applied to this team, null when the team has no deduction.
|
||||
/// </summary>
|
||||
public AppliedPointDeduction? PointDeduction { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class Scorer : EntityBase
|
||||
{
|
||||
public required Guid PlayerId { get; set; }
|
||||
|
||||
public Player? Player { get; set; }
|
||||
|
||||
public required int Points { get; set; }
|
||||
|
||||
public required Guid MatchId { get; set; }
|
||||
|
||||
public Match? Match { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a season in the Club12 application, a top-level grouping that gathers several tournaments played in the same competitive period.
|
||||
/// </summary>
|
||||
public class Season : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The display name of the season, in the form "Temporada XXVII".
|
||||
/// </summary>
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique, URL-friendly identifier used in public season links, generated once from the name and never changed afterward.
|
||||
/// </summary>
|
||||
public required string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The optional calendar year the season is played in.
|
||||
/// </summary>
|
||||
public int? Year { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The tournaments grouped under this season, empty until tournaments are assigned since membership is optional.
|
||||
/// </summary>
|
||||
public virtual ICollection<Tournament> Tournaments { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Domain.Enums;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class Stage : EntityBase
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique, URL-friendly identifier used in public stage links, generated once from the name and never changed afterward.
|
||||
/// </summary>
|
||||
public required string Slug { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
public required StageType StageType { get; set; }
|
||||
public required bool IsActive { get; set; }
|
||||
public bool IsElimination { get; set; }
|
||||
public required DateTime StartDate { get; set; }
|
||||
public required DateTime EndDate { get; set; }
|
||||
public required Guid DivisionId { get; set; }
|
||||
public required Division Division { get; set; }
|
||||
public virtual required ICollection<Match> Matches { get; set; } = [];
|
||||
public int Order { get; set; }
|
||||
public virtual ICollection<StageTeamMatch> StageTeamMatches { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Groups multiple parallel elimination brackets under the same division, null when the stage belongs to the division's default bracket.
|
||||
/// </summary>
|
||||
public string? BracketName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of games in a series between two teams at this round, defaulting to 1 for a single match deciding the round.
|
||||
/// </summary>
|
||||
public int BestOf { get; set; } = 1;
|
||||
public virtual ICollection<MatchSeries> MatchSeries { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// How many times each pair of teams plays within this group stage, defaulting to 1 for a single round-robin.
|
||||
/// </summary>
|
||||
public int RoundRobinLegs { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// When this bracket's seeding draw was committed, null until a draw runs.
|
||||
/// </summary>
|
||||
public DateTime? DrawnAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class StageTeamMatch : EntityBase
|
||||
{
|
||||
public required Guid StageId { get; set; }
|
||||
public Stage? Stage { get; set; }
|
||||
public required Guid TeamId { get; set; }
|
||||
public Team? Team { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class Team : EntityBase
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique, URL-friendly identifier used in public team links, generated once from the name and never changed afterward.
|
||||
/// </summary>
|
||||
public required string Slug { get; set; }
|
||||
|
||||
public required string ThreeLetterCode { get; set; }
|
||||
public required string LogoUrl { get; set; }
|
||||
public required string ShirtColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The jersey kit pattern applied over the primary ShirtColor, defaulting to solid.
|
||||
/// </summary>
|
||||
public string JerseyStyle { get; set; } = "solid";
|
||||
|
||||
/// <summary>
|
||||
/// Optional secondary hex color used for the jersey pattern or trim, null when the kit is a plain solid with no accent.
|
||||
/// </summary>
|
||||
public string? ShirtSecondaryColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional third hex color, used only by tri-color kit templates as a second accent alongside ShirtSecondaryColor.
|
||||
/// </summary>
|
||||
public string? ShirtTertiaryColor { get; set; }
|
||||
|
||||
public Tournament? Tournament { get; set; }
|
||||
public Guid? TournamentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional link to the team's stable cross-season identity, purely additive and never breaking existing per-season Team behavior.
|
||||
/// </summary>
|
||||
public Guid? ClubId { get; set; }
|
||||
public Club? Club { get; set; }
|
||||
/// <summary>
|
||||
/// Players whose denormalized current team pointer points here, not season-scoped.
|
||||
/// </summary>
|
||||
public virtual required ICollection<Player> Players { get; set; } = [];
|
||||
public virtual ICollection<StageTeamMatch> StageTeamMatches { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Every player ever registered to this team, across every season it has belonged to, the source of truth for roster membership.
|
||||
/// </summary>
|
||||
public virtual ICollection<PlayerTeamRegistration> PlayerTeamRegistrations { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Every tournament this team has ever been registered to, across every season, independent of the current TournamentId pointer.
|
||||
/// </summary>
|
||||
public virtual ICollection<TeamTournamentRegistration> TeamTournamentRegistrations { get; set; } = [];
|
||||
|
||||
public virtual ICollection<DivisionTeamRegistration> DivisionTeamRegistrations { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A disciplinary points penalty applied by an admin to a team within a division, subtracted from that team's standings total.
|
||||
/// </summary>
|
||||
public class TeamPointDeduction : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The division whose standings the penalty affects.
|
||||
/// </summary>
|
||||
public Guid DivisionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The division navigation, optional so the entity can be built from an id alone; deleting the division cascades its deductions away.
|
||||
/// </summary>
|
||||
public Division? Division { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The penalised team.
|
||||
/// </summary>
|
||||
public Guid TeamId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The team navigation, optional so the entity can be built from an id alone; loaded when the caller needs the team's name.
|
||||
/// </summary>
|
||||
public Team? Team { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The positive amount of table points to subtract from the team's total.
|
||||
/// </summary>
|
||||
public required int Points { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The disciplinary reason for the deduction, shown next to the penalised team in the standings.
|
||||
/// </summary>
|
||||
public required string Reason { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Domain.Enums;
|
||||
|
||||
using System;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A member of a team's technical staff, scoped to one team within one tournament, mirroring the season scoping used by PlayerTeamRegistration.
|
||||
/// </summary>
|
||||
public class TeamStaff : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The team this staff member belongs to.
|
||||
/// </summary>
|
||||
public Guid TeamId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The team navigation, optional so the entity can be built from an id alone; loaded when the caller needs the team's name.
|
||||
/// </summary>
|
||||
public Team? Team { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The season this staff membership belongs to.
|
||||
/// </summary>
|
||||
public Guid TournamentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The tournament navigation, optional so the entity can be built from an id alone.
|
||||
/// </summary>
|
||||
public Tournament? Tournament { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The staff member's full name.
|
||||
/// </summary>
|
||||
public required string FullName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The role this person holds on the team's technical staff.
|
||||
/// </summary>
|
||||
public required TeamStaffRole Role { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Links a Team to a Tournament for exactly one season, the source of truth for a team's participation history, unlike the denormalized Team.TournamentId pointer.
|
||||
/// </summary>
|
||||
public class TeamTournamentRegistration : EntityBase
|
||||
{
|
||||
public required Guid TeamId { get; set; }
|
||||
public Team? Team { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The season this registration belongs to, captured at registration time.
|
||||
/// </summary>
|
||||
public required Guid TournamentId { get; set; }
|
||||
public Tournament? Tournament { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Domain.Enums;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class Tournament : EntityBase
|
||||
{
|
||||
public required string Description { get; set; }
|
||||
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique, URL-friendly identifier used in public tournament links, generated once from the name and never changed afterward.
|
||||
/// </summary>
|
||||
public required string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The deadline for team registrations, which must be earlier than the tournament start date.
|
||||
/// </summary>
|
||||
public required DateTime TeamRegistrationDeadline { get; set; }
|
||||
|
||||
public required DateTime StartDate { get; set; }
|
||||
|
||||
public TournamentStatus Status { get; set; } = TournamentStatus.Scheduled;
|
||||
|
||||
/// <summary>
|
||||
/// Competitive gender category of the tournament, the source of truth for the no-mixing invariant across its divisions.
|
||||
/// </summary>
|
||||
public TournamentCategory Category { get; set; } = TournamentCategory.Masculine;
|
||||
|
||||
/// <summary>
|
||||
/// The optional id of the season this tournament belongs to, purely additive and never affecting the tournament's own Category.
|
||||
/// </summary>
|
||||
public Guid? SeasonId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The season this tournament belongs to, or null when it is not grouped under any season.
|
||||
/// </summary>
|
||||
public virtual Season? Season { get; set; }
|
||||
|
||||
public virtual required ICollection<Division> Divisions { get; set; }
|
||||
|
||||
public virtual required ICollection<Team> Teams { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Domain.Entities.Models;
|
||||
|
||||
public class Venue : EntityBase
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique, URL-friendly identifier used in public venue links, generated once from the name and never changed afterward.
|
||||
/// </summary>
|
||||
public required string Slug { get; set; }
|
||||
|
||||
public required string Address { get; set; }
|
||||
public string? PhotoUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional geographic latitude of the venue, used to build a public map link, null when the venue has no coordinates yet.
|
||||
/// </summary>
|
||||
public double? Latitude { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional geographic longitude of the venue, used to build a public map link, null when the venue has no coordinates yet.
|
||||
/// </summary>
|
||||
public double? Longitude { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// The sensitive, auditable actions tracked for traceability, persisted as the enum name so the trail survives numeric reordering.
|
||||
/// </summary>
|
||||
public enum AuditAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Full tournament-domain data wipe, triggered by DataMaintenanceService.
|
||||
/// </summary>
|
||||
DataWipe,
|
||||
|
||||
/// <summary>
|
||||
/// Database restore from a backup, triggered by BackupRestoreService.
|
||||
/// </summary>
|
||||
BackupRestore,
|
||||
|
||||
/// <summary>
|
||||
/// A tournament lifecycle status change, triggered by TournamentService.
|
||||
/// </summary>
|
||||
TournamentStatusChange,
|
||||
|
||||
/// <summary>
|
||||
/// An admin-triggered password reset, from user management.
|
||||
/// </summary>
|
||||
PasswordReset,
|
||||
|
||||
/// <summary>
|
||||
/// A bracket seeding draw, initial or re-draw, recorded for transparency.
|
||||
/// </summary>
|
||||
PlayoffDraw,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// How a catalogued database backup was created.
|
||||
/// </summary>
|
||||
public enum BackupOrigin
|
||||
{
|
||||
/// <summary>
|
||||
/// Triggered on-demand by an Admin from the Data Administration panel.
|
||||
/// </summary>
|
||||
Manual,
|
||||
|
||||
/// <summary>
|
||||
/// Created automatically by the scheduled DatabaseBackupHostedService.
|
||||
/// </summary>
|
||||
Job
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// How a playoffs-only bracket's first-round team order is produced: shuffled server-side or set by an admin.
|
||||
/// </summary>
|
||||
public enum DrawMode
|
||||
{
|
||||
Random,
|
||||
Manual,
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// The lifecycle state of a match's result, distinct from the boolean Match.IsFinished flag.
|
||||
/// </summary>
|
||||
public enum MatchStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The default state: the fixture exists but no result has been loaded yet.
|
||||
/// </summary>
|
||||
Scheduled,
|
||||
|
||||
/// <summary>
|
||||
/// The match was played and a decisive result was loaded.
|
||||
/// </summary>
|
||||
Played,
|
||||
|
||||
/// <summary>
|
||||
/// The match was postponed or suspended and has no result.
|
||||
/// </summary>
|
||||
Suspended,
|
||||
|
||||
/// <summary>
|
||||
/// One team did not show up, so the regulation default result was applied to the present team.
|
||||
/// </summary>
|
||||
WalkOver,
|
||||
|
||||
/// <summary>
|
||||
/// The match will never be played because its tournament was canceled or force-closed while it was still pending.
|
||||
/// </summary>
|
||||
Canceled
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
public enum MatchType
|
||||
{
|
||||
Regular,
|
||||
Playoff
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Medical-record eligibility status of a player's season registration, scoped per player, team, and tournament.
|
||||
/// </summary>
|
||||
public enum MedicalRecordStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Default for a brand-new registration: no medical record uploaded yet, or uploaded but not yet reviewed.
|
||||
/// </summary>
|
||||
Pending,
|
||||
|
||||
/// <summary>
|
||||
/// The owner or admin reviewed and approved the medical record, making the player habilitado for that team and tournament.
|
||||
/// </summary>
|
||||
Approved,
|
||||
|
||||
/// <summary>
|
||||
/// The owner or admin rejected the medical record, usually with a reason, leaving the player not habilitado.
|
||||
/// </summary>
|
||||
Rejected,
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Compile-time string constants for every UserRoleType value, used in authorization attributes to avoid magic strings.
|
||||
/// </summary>
|
||||
public static class Roles
|
||||
{
|
||||
public const string Admin = nameof(UserRoleType.ADMIN);
|
||||
public const string Owner = nameof(UserRoleType.OWNER);
|
||||
public const string Guest = nameof(UserRoleType.GUEST);
|
||||
|
||||
/// <summary>
|
||||
/// Every staff role that may operate the panel: the Owner account and the Admin IT account.
|
||||
/// </summary>
|
||||
public const string AdminOrOwner = $"{Admin},{Owner}";
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
public enum SanctionAppealStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// No appeal has been submitted.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// An appeal was submitted and is awaiting a decision.
|
||||
/// </summary>
|
||||
Pending,
|
||||
|
||||
/// <summary>
|
||||
/// The appeal was accepted and the sanction is overturned.
|
||||
/// </summary>
|
||||
Accepted,
|
||||
|
||||
/// <summary>
|
||||
/// The appeal was rejected and the sanction stands.
|
||||
/// </summary>
|
||||
Rejected
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// The kind of subject a PlayerSanction is applied to: an individual player, a whole team, or a staff member.
|
||||
/// </summary>
|
||||
public enum SanctionSubjectType
|
||||
{
|
||||
/// <summary>
|
||||
/// The sanction targets an individual player, identified by PlayerSanction.PlayerId.
|
||||
/// </summary>
|
||||
Player,
|
||||
|
||||
/// <summary>
|
||||
/// The sanction targets a whole team as an institutional sanction, identified by PlayerSanction.TeamId.
|
||||
/// </summary>
|
||||
Team,
|
||||
|
||||
/// <summary>
|
||||
/// The sanction targets a staff member, identified only by PlayerSanction.StaffName.
|
||||
/// </summary>
|
||||
Staff
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
public enum StageType
|
||||
{
|
||||
/// <summary>
|
||||
/// Group stage, where teams play in a round-robin format.
|
||||
/// </summary>
|
||||
Group,
|
||||
|
||||
/// <summary>
|
||||
/// Round of 16 stage.
|
||||
/// </summary>
|
||||
RoundOf16,
|
||||
|
||||
QuarterFinal,
|
||||
SemiFinal,
|
||||
ThirdPlace,
|
||||
Final
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
public enum StatisticType
|
||||
{
|
||||
Points,
|
||||
Assists
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// The role a member of a team's technical staff holds for a given season.
|
||||
/// </summary>
|
||||
public enum TeamStaffRole
|
||||
{
|
||||
/// <summary>
|
||||
/// Head coach.
|
||||
/// </summary>
|
||||
Coach,
|
||||
|
||||
/// <summary>
|
||||
/// Assistant coach.
|
||||
/// </summary>
|
||||
AssistantCoach
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// The group-stage tiebreaker criteria, in the exact priority order the club's rulebook applies them.
|
||||
/// </summary>
|
||||
public enum TiebreakerCriterion
|
||||
{
|
||||
/// <summary>
|
||||
/// Table points awarded for wins and losses.
|
||||
/// </summary>
|
||||
Points,
|
||||
|
||||
/// <summary>
|
||||
/// Games won across the whole zone.
|
||||
/// </summary>
|
||||
GamesWon,
|
||||
|
||||
/// <summary>
|
||||
/// Points for minus points against, across the whole zone.
|
||||
/// </summary>
|
||||
PointsDifference,
|
||||
|
||||
/// <summary>
|
||||
/// Head-to-head result among the tied teams.
|
||||
/// </summary>
|
||||
HeadToHead,
|
||||
|
||||
/// <summary>
|
||||
/// Points difference considering only the games played among the tied teams.
|
||||
/// </summary>
|
||||
HeadToHeadPointsDifference,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Competitive gender category of a tournament, living on the Tournament as the source of truth every division must share.
|
||||
/// </summary>
|
||||
public enum TournamentCategory
|
||||
{
|
||||
/// <summary>
|
||||
/// Masculine competition, the default for existing or unspecified tournaments.
|
||||
/// </summary>
|
||||
Masculine = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Feminine competition, played as its own separate tournament.
|
||||
/// </summary>
|
||||
Feminine = 1
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Lifecycle status of a Tournament; see TournamentStatusTransitions for the allowed moves between these values.
|
||||
/// </summary>
|
||||
public enum TournamentStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The tournament is scheduled but not yet open for team registrations.
|
||||
/// </summary>
|
||||
Scheduled,
|
||||
OpenForRegistration,
|
||||
/// <summary>
|
||||
/// Registration has closed, freezing structural changes: the roster is fixed and teams are assigned to divisions.
|
||||
/// </summary>
|
||||
RegistrationClosed,
|
||||
Ongoing,
|
||||
Finished,
|
||||
/// <summary>
|
||||
/// The tournament has been canceled and will not take place.
|
||||
/// </summary>
|
||||
Canceled,
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the tournament lifecycle state machine, a linear happy path from Scheduled through Finished plus a Canceled escape from any non-terminal state.
|
||||
/// </summary>
|
||||
public static class TournamentStatusTransitions
|
||||
{
|
||||
/// <summary>
|
||||
/// The set of states each status may move to next, an absent status meaning an invalid transition.
|
||||
/// </summary>
|
||||
private static readonly IReadOnlyDictionary<TournamentStatus, TournamentStatus[]> AllowedNextStates =
|
||||
new Dictionary<TournamentStatus, TournamentStatus[]>
|
||||
{
|
||||
[TournamentStatus.Scheduled] = [TournamentStatus.OpenForRegistration, TournamentStatus.Canceled],
|
||||
[TournamentStatus.OpenForRegistration] = [TournamentStatus.RegistrationClosed, TournamentStatus.Canceled],
|
||||
[TournamentStatus.RegistrationClosed] = [TournamentStatus.Ongoing, TournamentStatus.Canceled],
|
||||
// Ongoing may revert to RegistrationClosed to fix a division mis-assignment, tearing down the generated fixture so restarting rebuilds it correctly.
|
||||
[TournamentStatus.Ongoing] = [TournamentStatus.Finished, TournamentStatus.RegistrationClosed, TournamentStatus.Canceled],
|
||||
[TournamentStatus.Finished] = [],
|
||||
[TournamentStatus.Canceled] = [],
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Whether a status is terminal, meaning it has no outgoing transition.
|
||||
/// </summary>
|
||||
public static bool IsTerminal(TournamentStatus status) =>
|
||||
status is TournamentStatus.Finished or TournamentStatus.Canceled;
|
||||
|
||||
/// <summary>
|
||||
/// Whether transitioning from one given status to another is allowed, with a same-status no-op always considered valid.
|
||||
/// </summary>
|
||||
public static bool IsValidTransition(TournamentStatus from, TournamentStatus to)
|
||||
{
|
||||
if (from == to)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return AllowedNextStates.TryGetValue(from, out TournamentStatus[]? nextStates)
|
||||
&& nextStates.Contains(to);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
public enum UserRoleType
|
||||
{
|
||||
ADMIN = 0,
|
||||
OWNER = 1,
|
||||
GUEST = 2
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Domain.Enums;
|
||||
|
||||
public static class UserRoleTypeExtensions
|
||||
{
|
||||
public static string ToRoleName(this UserRoleType role)
|
||||
{
|
||||
return role.ToString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user