diff --git a/SPMS.Domain/Common/ErrorCodes.cs b/SPMS.Domain/Common/ErrorCodes.cs
new file mode 100644
index 0000000..0194968
--- /dev/null
+++ b/SPMS.Domain/Common/ErrorCodes.cs
@@ -0,0 +1,33 @@
+namespace SPMS.Domain.Common;
+
+///
+/// SPMS 에러 코드 상수
+/// 코드 체계: [상태(0=성공,1=실패)][도메인(0~8)][순번]
+///
+public static class ErrorCodes
+{
+ // === 성공 ===
+ public const string Success = "000";
+
+ // === 공통 (0) ===
+ public const string BadRequest = "101";
+ public const string Unauthorized = "102";
+ public const string NotFound = "103";
+ public const string InternalError = "104";
+ public const string NoChange = "105";
+ public const string LimitExceeded = "106";
+ public const string Conflict = "107";
+
+ // === Auth (1) ===
+ public const string VerificationCodeError = "111";
+ public const string LoginFailed = "112";
+ public const string LoginAttemptExceeded = "113";
+
+ // === Account (2) ===
+ public const string PasswordValidationFailed = "121";
+ public const string ResetTokenError = "122";
+
+ // === Push (6) ===
+ public const string PushSendFailed = "161";
+ public const string PushStateChangeNotAllowed = "162";
+}
diff --git a/SPMS.Domain/Entities/Admin.cs b/SPMS.Domain/Entities/Admin.cs
index 6ebbe9e..351243b 100644
--- a/SPMS.Domain/Entities/Admin.cs
+++ b/SPMS.Domain/Entities/Admin.cs
@@ -1,3 +1,5 @@
+using SPMS.Domain.Enums;
+
namespace SPMS.Domain.Entities;
public class Admin : BaseEntity
@@ -7,7 +9,7 @@ public class Admin : BaseEntity
public string Password { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
- public byte Role { get; set; }
+ public AdminRole Role { get; set; }
public bool EmailVerified { get; set; }
public DateTime? EmailVerifiedAt { get; set; }
public DateTime CreatedAt { get; set; }
diff --git a/SPMS.Domain/Entities/Device.cs b/SPMS.Domain/Entities/Device.cs
index 2955de0..eb8aa3c 100644
--- a/SPMS.Domain/Entities/Device.cs
+++ b/SPMS.Domain/Entities/Device.cs
@@ -1,10 +1,12 @@
+using SPMS.Domain.Enums;
+
namespace SPMS.Domain.Entities;
public class Device : BaseEntity
{
public long ServiceId { get; set; }
public string DeviceToken { get; set; } = string.Empty;
- public byte Platform { get; set; }
+ public Platform Platform { get; set; }
public string? AppVersion { get; set; }
public string? OsVersion { get; set; }
public string? DeviceModel { get; set; }
diff --git a/SPMS.Domain/Entities/Payment.cs b/SPMS.Domain/Entities/Payment.cs
index 5d0497c..ab3939e 100644
--- a/SPMS.Domain/Entities/Payment.cs
+++ b/SPMS.Domain/Entities/Payment.cs
@@ -1,3 +1,5 @@
+using SPMS.Domain.Enums;
+
namespace SPMS.Domain.Entities;
public class Payment : BaseEntity
@@ -8,9 +10,9 @@ public class Payment : BaseEntity
public string Currency { get; set; } = string.Empty;
public string? PaymentMethod { get; set; }
public string? PaymentKey { get; set; }
- public byte Status { get; set; }
- public byte? TierBefore { get; set; }
- public byte TierAfter { get; set; }
+ public PaymentStatus Status { get; set; }
+ public SubTier? TierBefore { get; set; }
+ public SubTier TierAfter { get; set; }
public DateTime PaidAt { get; set; }
public DateTime CreatedAt { get; set; }
diff --git a/SPMS.Domain/Entities/PushSendLog.cs b/SPMS.Domain/Entities/PushSendLog.cs
index d96554a..d647961 100644
--- a/SPMS.Domain/Entities/PushSendLog.cs
+++ b/SPMS.Domain/Entities/PushSendLog.cs
@@ -1,3 +1,5 @@
+using SPMS.Domain.Enums;
+
namespace SPMS.Domain.Entities;
public class PushSendLog : BaseEntity
@@ -5,7 +7,7 @@ public class PushSendLog : BaseEntity
public long ServiceId { get; set; }
public long MessageId { get; set; }
public long DeviceId { get; set; }
- public byte Status { get; set; }
+ public PushResult Status { get; set; }
public string? FailReason { get; set; }
public DateTime SentAt { get; set; }
diff --git a/SPMS.Domain/Entities/Service.cs b/SPMS.Domain/Entities/Service.cs
index 8366f9d..73be73b 100644
--- a/SPMS.Domain/Entities/Service.cs
+++ b/SPMS.Domain/Entities/Service.cs
@@ -1,3 +1,5 @@
+using SPMS.Domain.Enums;
+
namespace SPMS.Domain.Entities;
public class Service : BaseEntity
@@ -14,9 +16,9 @@ public class Service : BaseEntity
public string? FcmCredentials { get; set; }
public string? WebhookUrl { get; set; }
public string? Tags { get; set; }
- public byte SubTier { get; set; }
+ public SubTier SubTier { get; set; }
public DateTime? SubStartedAt { get; set; }
- public byte Status { get; set; }
+ public ServiceStatus Status { get; set; }
public DateTime CreatedAt { get; set; }
public long CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
diff --git a/SPMS.Domain/Entities/WebhookLog.cs b/SPMS.Domain/Entities/WebhookLog.cs
index ed16fa7..f2fcaa6 100644
--- a/SPMS.Domain/Entities/WebhookLog.cs
+++ b/SPMS.Domain/Entities/WebhookLog.cs
@@ -1,12 +1,14 @@
+using SPMS.Domain.Enums;
+
namespace SPMS.Domain.Entities;
public class WebhookLog : BaseEntity
{
public long ServiceId { get; set; }
public string WebhookUrl { get; set; } = string.Empty;
- public string EventType { get; set; } = string.Empty;
+ public WebhookEvent EventType { get; set; }
public string Payload { get; set; } = string.Empty;
- public byte Status { get; set; }
+ public WebhookStatus Status { get; set; }
public int? ResponseCode { get; set; }
public string? ResponseBody { get; set; }
public DateTime SentAt { get; set; }
diff --git a/SPMS.Domain/Enums/AdminRole.cs b/SPMS.Domain/Enums/AdminRole.cs
new file mode 100644
index 0000000..81330e9
--- /dev/null
+++ b/SPMS.Domain/Enums/AdminRole.cs
@@ -0,0 +1,11 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 관리자 권한 (Admin.role)
+///
+public enum AdminRole : byte
+{
+ Super = 0,
+ Manager = 1,
+ User = 2
+}
diff --git a/SPMS.Domain/Enums/DeviceStatus.cs b/SPMS.Domain/Enums/DeviceStatus.cs
new file mode 100644
index 0000000..8b25766
--- /dev/null
+++ b/SPMS.Domain/Enums/DeviceStatus.cs
@@ -0,0 +1,11 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 디바이스 상태
+///
+public enum DeviceStatus : byte
+{
+ Active = 0,
+ Inactive = 1,
+ Blocked = 2
+}
diff --git a/SPMS.Domain/Enums/LinkType.cs b/SPMS.Domain/Enums/LinkType.cs
new file mode 100644
index 0000000..606fdea
--- /dev/null
+++ b/SPMS.Domain/Enums/LinkType.cs
@@ -0,0 +1,11 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 링크 유형
+///
+public enum LinkType : byte
+{
+ App = 0,
+ Web = 1,
+ DeepLink = 2
+}
diff --git a/SPMS.Domain/Enums/MessageStatus.cs b/SPMS.Domain/Enums/MessageStatus.cs
new file mode 100644
index 0000000..98a9675
--- /dev/null
+++ b/SPMS.Domain/Enums/MessageStatus.cs
@@ -0,0 +1,14 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 메시지 발송 상태
+///
+public enum MessageStatus : byte
+{
+ Draft = 0,
+ Pending = 1,
+ Sending = 2,
+ Sent = 3,
+ Failed = 4,
+ Cancelled = 5
+}
diff --git a/SPMS.Domain/Enums/PaymentStatus.cs b/SPMS.Domain/Enums/PaymentStatus.cs
new file mode 100644
index 0000000..3fdc096
--- /dev/null
+++ b/SPMS.Domain/Enums/PaymentStatus.cs
@@ -0,0 +1,11 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 결제 상태 (Payment.status)
+///
+public enum PaymentStatus : byte
+{
+ Completed = 0,
+ Cancelled = 1,
+ Refunded = 2
+}
diff --git a/SPMS.Domain/Enums/Platform.cs b/SPMS.Domain/Enums/Platform.cs
new file mode 100644
index 0000000..b6f3673
--- /dev/null
+++ b/SPMS.Domain/Enums/Platform.cs
@@ -0,0 +1,11 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 디바이스 플랫폼 (Device.platform)
+///
+public enum Platform : byte
+{
+ iOS = 0,
+ Android = 1,
+ Web = 2
+}
diff --git a/SPMS.Domain/Enums/PushResult.cs b/SPMS.Domain/Enums/PushResult.cs
new file mode 100644
index 0000000..2b4a121
--- /dev/null
+++ b/SPMS.Domain/Enums/PushResult.cs
@@ -0,0 +1,10 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 푸시 발송 결과 (PushSendLog.status)
+///
+public enum PushResult : byte
+{
+ Success = 0,
+ Failed = 1
+}
diff --git a/SPMS.Domain/Enums/ServiceStatus.cs b/SPMS.Domain/Enums/ServiceStatus.cs
new file mode 100644
index 0000000..e8c4c09
--- /dev/null
+++ b/SPMS.Domain/Enums/ServiceStatus.cs
@@ -0,0 +1,10 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 서비스 상태 (Service.status)
+///
+public enum ServiceStatus : byte
+{
+ Active = 0,
+ Suspended = 1
+}
diff --git a/SPMS.Domain/Enums/SubTier.cs b/SPMS.Domain/Enums/SubTier.cs
new file mode 100644
index 0000000..cb2d18f
--- /dev/null
+++ b/SPMS.Domain/Enums/SubTier.cs
@@ -0,0 +1,11 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 구독 티어 (Service.sub_tier)
+///
+public enum SubTier : byte
+{
+ Free = 0,
+ Basic = 1,
+ Pro = 2
+}
diff --git a/SPMS.Domain/Enums/TargetType.cs b/SPMS.Domain/Enums/TargetType.cs
new file mode 100644
index 0000000..f722c5d
--- /dev/null
+++ b/SPMS.Domain/Enums/TargetType.cs
@@ -0,0 +1,12 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 발송 대상 유형
+///
+public enum TargetType : byte
+{
+ All = 0,
+ Filter = 1,
+ CsvFile = 2,
+ UserList = 3
+}
diff --git a/SPMS.Domain/Enums/WebhookEvent.cs b/SPMS.Domain/Enums/WebhookEvent.cs
new file mode 100644
index 0000000..d60b0dc
--- /dev/null
+++ b/SPMS.Domain/Enums/WebhookEvent.cs
@@ -0,0 +1,11 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 웹훅 이벤트 유형 (WebhookLog.event_type)
+///
+public enum WebhookEvent : byte
+{
+ PushSent = 0,
+ PushFailed = 1,
+ PushClicked = 2
+}
diff --git a/SPMS.Domain/Enums/WebhookStatus.cs b/SPMS.Domain/Enums/WebhookStatus.cs
new file mode 100644
index 0000000..b2f92f5
--- /dev/null
+++ b/SPMS.Domain/Enums/WebhookStatus.cs
@@ -0,0 +1,10 @@
+namespace SPMS.Domain.Enums;
+
+///
+/// 웹훅 발송 결과 (WebhookLog.status)
+///
+public enum WebhookStatus : byte
+{
+ Success = 0,
+ Failed = 1
+}
diff --git a/SPMS.Infrastructure/Migrations/20260209042244_ApplyDomainEnums.Designer.cs b/SPMS.Infrastructure/Migrations/20260209042244_ApplyDomainEnums.Designer.cs
new file mode 100644
index 0000000..4b8c815
--- /dev/null
+++ b/SPMS.Infrastructure/Migrations/20260209042244_ApplyDomainEnums.Designer.cs
@@ -0,0 +1,837 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using SPMS.Infrastructure;
+
+#nullable disable
+
+namespace SPMS.Infrastructure.Migrations
+{
+ [DbContext(typeof(AppDbContext))]
+ [Migration("20260209042244_ApplyDomainEnums")]
+ partial class ApplyDomainEnums
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
+
+ modelBuilder.Entity("SPMS.Domain.Entities.Admin", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("AdminCode")
+ .IsRequired()
+ .HasMaxLength(8)
+ .HasColumnType("varchar(8)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("EmailVerified")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(false);
+
+ b.Property("EmailVerifiedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("IsDeleted")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(false);
+
+ b.Property("LastLoginAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("varchar(64)");
+
+ b.Property("Phone")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("Role")
+ .HasColumnType("tinyint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AdminCode")
+ .IsUnique();
+
+ b.HasIndex("Email")
+ .IsUnique();
+
+ b.ToTable("Admin", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.DailyStat", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("FailCnt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasDefaultValue(0);
+
+ b.Property("OpenCnt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasDefaultValue(0);
+
+ b.Property("SentCnt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasDefaultValue(0);
+
+ b.Property("ServiceId")
+ .HasColumnType("bigint");
+
+ b.Property("StatDate")
+ .HasColumnType("date");
+
+ b.Property("SuccessCnt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasDefaultValue(0);
+
+ b.HasKey("Id");
+
+ b.HasIndex("ServiceId", "StatDate")
+ .IsUnique();
+
+ b.ToTable("DailyStat", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.Device", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("AgreeUpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("AppVersion")
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("DeviceModel")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("DeviceToken")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("varchar(255)");
+
+ b.Property("IsActive")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(true);
+
+ b.Property("MarketingAgreed")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("MktAgreeUpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("OsVersion")
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("Platform")
+ .HasColumnType("tinyint");
+
+ b.Property("PushAgreed")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("ServiceId")
+ .HasColumnType("bigint");
+
+ b.Property("Tags")
+ .HasColumnType("json");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ServiceId", "DeviceToken");
+
+ b.ToTable("Device", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.FileEntity", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CreatedBy")
+ .HasColumnType("bigint");
+
+ b.Property("FileName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("FilePath")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("varchar(500)");
+
+ b.Property("FileSize")
+ .HasColumnType("bigint");
+
+ b.Property("FileType")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("MimeType")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("ServiceId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedBy");
+
+ b.HasIndex("ServiceId");
+
+ b.ToTable("File", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.Message", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("Body")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("varchar(500)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CreatedBy")
+ .HasColumnType("bigint");
+
+ b.Property("CustomData")
+ .HasColumnType("json");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("ImageUrl")
+ .HasMaxLength(500)
+ .HasColumnType("varchar(500)");
+
+ b.Property("IsDeleted")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(false);
+
+ b.Property("LinkUrl")
+ .HasMaxLength(500)
+ .HasColumnType("varchar(500)");
+
+ b.Property("MessageCode")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("varchar(10)");
+
+ b.Property("ServiceId")
+ .HasColumnType("bigint");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedBy");
+
+ b.HasIndex("MessageCode")
+ .IsUnique();
+
+ b.HasIndex("ServiceId");
+
+ b.ToTable("Message", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.Payment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("AdminId")
+ .HasColumnType("bigint");
+
+ b.Property("Amount")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Currency")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("varchar(10)");
+
+ b.Property("PaidAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("PaymentKey")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("PaymentMethod")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("ServiceId")
+ .HasColumnType("bigint");
+
+ b.Property("Status")
+ .HasColumnType("tinyint");
+
+ b.Property("TierAfter")
+ .HasColumnType("tinyint");
+
+ b.Property("TierBefore")
+ .HasColumnType("tinyint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AdminId");
+
+ b.HasIndex("ServiceId");
+
+ b.ToTable("Payment", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.PushOpenLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("DeviceId")
+ .HasColumnType("bigint");
+
+ b.Property("MessageId")
+ .HasColumnType("bigint");
+
+ b.Property("OpenedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("ServiceId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DeviceId");
+
+ b.HasIndex("MessageId");
+
+ b.HasIndex("ServiceId", "OpenedAt");
+
+ b.ToTable("PushOpenLog", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.PushSendLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("DeviceId")
+ .HasColumnType("bigint");
+
+ b.Property("FailReason")
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("MessageId")
+ .HasColumnType("bigint");
+
+ b.Property("SentAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("ServiceId")
+ .HasColumnType("bigint");
+
+ b.Property("Status")
+ .HasColumnType("tinyint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DeviceId");
+
+ b.HasIndex("MessageId");
+
+ b.HasIndex("ServiceId", "SentAt");
+
+ b.ToTable("PushSendLog", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.Service", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("ApiKey")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("varchar(64)");
+
+ b.Property("ApiKeyCreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("ApnsBundleId")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("ApnsKeyId")
+ .HasMaxLength(10)
+ .HasColumnType("varchar(10)");
+
+ b.Property("ApnsPrivateKey")
+ .HasColumnType("text");
+
+ b.Property("ApnsTeamId")
+ .HasMaxLength(10)
+ .HasColumnType("varchar(10)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CreatedBy")
+ .HasColumnType("bigint");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Description")
+ .HasMaxLength(500)
+ .HasColumnType("varchar(500)");
+
+ b.Property("FcmCredentials")
+ .HasColumnType("text");
+
+ b.Property("IsDeleted")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(false);
+
+ b.Property("ServiceCode")
+ .IsRequired()
+ .HasMaxLength(8)
+ .HasColumnType("varchar(8)");
+
+ b.Property("ServiceName")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("Status")
+ .HasColumnType("tinyint");
+
+ b.Property("SubStartedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("SubTier")
+ .HasColumnType("tinyint");
+
+ b.Property("Tags")
+ .HasColumnType("json");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("WebhookUrl")
+ .HasMaxLength(500)
+ .HasColumnType("varchar(500)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedBy");
+
+ b.HasIndex("ServiceCode")
+ .IsUnique();
+
+ b.ToTable("Service", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.ServiceIp", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("IpAddress")
+ .IsRequired()
+ .HasMaxLength(45)
+ .HasColumnType("varchar(45)");
+
+ b.Property("ServiceId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ServiceId");
+
+ b.ToTable("ServiceIp", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.SystemLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("Action")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("AdminId")
+ .HasColumnType("bigint");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Details")
+ .HasColumnType("json");
+
+ b.Property("IpAddress")
+ .HasMaxLength(45)
+ .HasColumnType("varchar(45)");
+
+ b.Property("ServiceId")
+ .HasColumnType("bigint");
+
+ b.Property("TargetId")
+ .HasColumnType("bigint");
+
+ b.Property("TargetType")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AdminId");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("ServiceId");
+
+ b.ToTable("SystemLog", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.WebhookLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("EventType")
+ .HasColumnType("tinyint");
+
+ b.Property("Payload")
+ .IsRequired()
+ .HasColumnType("json");
+
+ b.Property("ResponseBody")
+ .HasColumnType("text");
+
+ b.Property("ResponseCode")
+ .HasColumnType("int");
+
+ b.Property("SentAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("ServiceId")
+ .HasColumnType("bigint");
+
+ b.Property("Status")
+ .HasColumnType("tinyint");
+
+ b.Property("WebhookUrl")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("varchar(500)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ServiceId", "SentAt");
+
+ b.ToTable("WebhookLog", (string)null);
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.DailyStat", b =>
+ {
+ b.HasOne("SPMS.Domain.Entities.Service", "Service")
+ .WithMany()
+ .HasForeignKey("ServiceId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Service");
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.Device", b =>
+ {
+ b.HasOne("SPMS.Domain.Entities.Service", "Service")
+ .WithMany("Devices")
+ .HasForeignKey("ServiceId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Service");
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.FileEntity", b =>
+ {
+ b.HasOne("SPMS.Domain.Entities.Admin", "CreatedByAdmin")
+ .WithMany()
+ .HasForeignKey("CreatedBy")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.HasOne("SPMS.Domain.Entities.Service", "Service")
+ .WithMany()
+ .HasForeignKey("ServiceId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("CreatedByAdmin");
+
+ b.Navigation("Service");
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.Message", b =>
+ {
+ b.HasOne("SPMS.Domain.Entities.Admin", "CreatedByAdmin")
+ .WithMany()
+ .HasForeignKey("CreatedBy")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.HasOne("SPMS.Domain.Entities.Service", "Service")
+ .WithMany("Messages")
+ .HasForeignKey("ServiceId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("CreatedByAdmin");
+
+ b.Navigation("Service");
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.Payment", b =>
+ {
+ b.HasOne("SPMS.Domain.Entities.Admin", "Admin")
+ .WithMany()
+ .HasForeignKey("AdminId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.HasOne("SPMS.Domain.Entities.Service", "Service")
+ .WithMany()
+ .HasForeignKey("ServiceId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Admin");
+
+ b.Navigation("Service");
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.PushOpenLog", b =>
+ {
+ b.HasOne("SPMS.Domain.Entities.Device", "Device")
+ .WithMany()
+ .HasForeignKey("DeviceId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.HasOne("SPMS.Domain.Entities.Message", "Message")
+ .WithMany()
+ .HasForeignKey("MessageId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.HasOne("SPMS.Domain.Entities.Service", "Service")
+ .WithMany()
+ .HasForeignKey("ServiceId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Device");
+
+ b.Navigation("Message");
+
+ b.Navigation("Service");
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.PushSendLog", b =>
+ {
+ b.HasOne("SPMS.Domain.Entities.Device", "Device")
+ .WithMany()
+ .HasForeignKey("DeviceId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.HasOne("SPMS.Domain.Entities.Message", "Message")
+ .WithMany()
+ .HasForeignKey("MessageId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.HasOne("SPMS.Domain.Entities.Service", "Service")
+ .WithMany()
+ .HasForeignKey("ServiceId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Device");
+
+ b.Navigation("Message");
+
+ b.Navigation("Service");
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.Service", b =>
+ {
+ b.HasOne("SPMS.Domain.Entities.Admin", "CreatedByAdmin")
+ .WithMany()
+ .HasForeignKey("CreatedBy")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("CreatedByAdmin");
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.ServiceIp", b =>
+ {
+ b.HasOne("SPMS.Domain.Entities.Service", "Service")
+ .WithMany("ServiceIps")
+ .HasForeignKey("ServiceId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Service");
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.SystemLog", b =>
+ {
+ b.HasOne("SPMS.Domain.Entities.Admin", "Admin")
+ .WithMany()
+ .HasForeignKey("AdminId")
+ .OnDelete(DeleteBehavior.Restrict);
+
+ b.HasOne("SPMS.Domain.Entities.Service", "Service")
+ .WithMany()
+ .HasForeignKey("ServiceId")
+ .OnDelete(DeleteBehavior.Restrict);
+
+ b.Navigation("Admin");
+
+ b.Navigation("Service");
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.WebhookLog", b =>
+ {
+ b.HasOne("SPMS.Domain.Entities.Service", "Service")
+ .WithMany()
+ .HasForeignKey("ServiceId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Service");
+ });
+
+ modelBuilder.Entity("SPMS.Domain.Entities.Service", b =>
+ {
+ b.Navigation("Devices");
+
+ b.Navigation("Messages");
+
+ b.Navigation("ServiceIps");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/SPMS.Infrastructure/Migrations/20260209042244_ApplyDomainEnums.cs b/SPMS.Infrastructure/Migrations/20260209042244_ApplyDomainEnums.cs
new file mode 100644
index 0000000..aac71d3
--- /dev/null
+++ b/SPMS.Infrastructure/Migrations/20260209042244_ApplyDomainEnums.cs
@@ -0,0 +1,38 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace SPMS.Infrastructure.Migrations
+{
+ ///
+ public partial class ApplyDomainEnums : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AlterColumn(
+ name: "EventType",
+ table: "WebhookLog",
+ type: "tinyint",
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "varchar(50)",
+ oldMaxLength: 50)
+ .OldAnnotation("MySql:CharSet", "utf8mb4");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AlterColumn(
+ name: "EventType",
+ table: "WebhookLog",
+ type: "varchar(50)",
+ maxLength: 50,
+ nullable: false,
+ oldClrType: typeof(sbyte),
+ oldType: "tinyint")
+ .Annotation("MySql:CharSet", "utf8mb4");
+ }
+ }
+}
diff --git a/SPMS.Infrastructure/Migrations/AppDbContextModelSnapshot.cs b/SPMS.Infrastructure/Migrations/AppDbContextModelSnapshot.cs
index d91db2f..bad846c 100644
--- a/SPMS.Infrastructure/Migrations/AppDbContextModelSnapshot.cs
+++ b/SPMS.Infrastructure/Migrations/AppDbContextModelSnapshot.cs
@@ -603,10 +603,8 @@ namespace SPMS.Infrastructure.Migrations
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
- b.Property("EventType")
- .IsRequired()
- .HasMaxLength(50)
- .HasColumnType("varchar(50)");
+ b.Property("EventType")
+ .HasColumnType("tinyint");
b.Property("Payload")
.IsRequired()
diff --git a/SPMS.Infrastructure/Persistence/Configurations/WebhookLogConfiguration.cs b/SPMS.Infrastructure/Persistence/Configurations/WebhookLogConfiguration.cs
index 8043923..a2e5989 100644
--- a/SPMS.Infrastructure/Persistence/Configurations/WebhookLogConfiguration.cs
+++ b/SPMS.Infrastructure/Persistence/Configurations/WebhookLogConfiguration.cs
@@ -15,7 +15,7 @@ public class WebhookLogConfiguration : IEntityTypeConfiguration
builder.Property(e => e.ServiceId).IsRequired();
builder.Property(e => e.WebhookUrl).HasMaxLength(500).IsRequired();
- builder.Property(e => e.EventType).HasMaxLength(50).IsRequired();
+ builder.Property(e => e.EventType).HasColumnType("tinyint").IsRequired();
builder.Property(e => e.Payload).HasColumnType("json").IsRequired();
builder.Property(e => e.Status).HasColumnType("tinyint").IsRequired();
builder.Property(e => e.ResponseCode);