USE [RobotInstaller]; GO SET ANSI_NULLS ON; SET QUOTED_IDENTIFIER ON; GO -- Additive/idempotent migration. Safe to run more than once. IF OBJECT_ID(N'dbo.Notifications', N'U') IS NULL BEGIN CREATE TABLE dbo.Notifications ( Id UNIQUEIDENTIFIER NOT NULL CONSTRAINT PK_Notifications PRIMARY KEY CLUSTERED CONSTRAINT DF_Notifications_Id DEFAULT NEWSEQUENTIALID(), RecipientUserId UNIQUEIDENTIFIER NOT NULL, ActorUserId UNIQUEIDENTIFIER NULL, ActorName NVARCHAR(200) NULL, BroadcastId UNIQUEIDENTIFIER NULL, EventType NVARCHAR(100) NOT NULL, Severity NVARCHAR(20) NOT NULL CONSTRAINT DF_Notifications_Severity DEFAULT N'info', Title NVARCHAR(200) NOT NULL, Message NVARCHAR(1000) NOT NULL, EntityType NVARCHAR(50) NULL, EntityId NVARCHAR(100) NULL, ActionUrl NVARCHAR(1000) NULL, ReadAt DATETIME2(3) NULL, CreatedAt DATETIME2(3) NOT NULL CONSTRAINT DF_Notifications_CreatedAt DEFAULT SYSUTCDATETIME(), ExpiresAt DATETIME2(3) NULL, CONSTRAINT FK_Notifications_RecipientUser FOREIGN KEY (RecipientUserId) REFERENCES dbo.Users(Id) ON DELETE CASCADE, CONSTRAINT CK_Notifications_Severity CHECK (Severity IN (N'info', N'success', N'warning', N'error')), CONSTRAINT CK_Notifications_EventType_NotBlank CHECK (LEN(LTRIM(RTRIM(EventType))) > 0), CONSTRAINT CK_Notifications_Title_NotBlank CHECK (LEN(LTRIM(RTRIM(Title))) > 0), CONSTRAINT CK_Notifications_Message_NotBlank CHECK (LEN(LTRIM(RTRIM(Message))) > 0) ); END; GO IF NOT EXISTS ( SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'dbo.Notifications') AND name = N'IX_Notifications_Recipient_Read_Created' ) BEGIN CREATE INDEX IX_Notifications_Recipient_Read_Created ON dbo.Notifications(RecipientUserId, ReadAt, CreatedAt DESC) INCLUDE (Severity, Title, ActionUrl, ExpiresAt); END; GO IF NOT EXISTS ( SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'dbo.Notifications') AND name = N'IX_Notifications_BroadcastId' ) BEGIN CREATE INDEX IX_Notifications_BroadcastId ON dbo.Notifications(BroadcastId) WHERE BroadcastId IS NOT NULL; END; GO PRINT N'RobotInstaller notification schema is ready.'; GO